diff --git a/.gitignore b/.gitignore
index 00530e1c83fc6858a098fb4c55d71090643c9b7f..3f02437e02e80eb03aa1bdc0a0c90ad51a2a112e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,7 @@ stage
 
 # Ignore packaged plugins and language packs
 *.phar
+
+# Ignore mPDF temp files
+include/mpdf/ttfontdata
+include/mpdf/tmp
diff --git a/README.md b/README.md
index c22e9da10a3da146855b4e979ac6a46d4848f943..9b6bb407c87429ebe55302a33392945ab7291c8a 100644
--- a/README.md
+++ b/README.md
@@ -93,6 +93,18 @@ Create your own fork of the project and use
 the feature is published in your fork, send a pull request to begin the
 conversation of integrating your new feature into osTicket.
 
+### Localization
+[![Crowdin](https://d322cqt584bo4o.cloudfront.net/osticket-official/localized.png)](http://i18n.osticket.com/project/osticket-official)
+
+The interface for osTicket is now completely translatable. Language packs
+are available on the [download page](http://osticket.com/download). If you
+do not see your language there, join the [Crowdin](http://i18n.osticket.com)
+project and request to have your language added. Languages which reach 100%
+translated are are significantly reviewed will be made available on the
+osTicket download page.
+
+Localizing strings in new code requires usage of a [few rules](setup/doc/i18n.md).
+
 License
 -------
 osTicket is released under the GPL2 license. See the included LICENSE.txt
@@ -111,5 +123,6 @@ osTicket is supported by several magical open source projects including:
   * [PEAR/Net_SMTP](http://pear.php.net/package/Net_SMTP)
   * [PEAR/Net_Socket](http://pear.php.net/package/Net_Socket)
   * [PEAR/Serivces_JSON](http://pear.php.net/package/Services_JSON)
+  * [php-gettext](https://launchpad.net/php-gettext/)
   * [phpseclib](http://phpseclib.sourceforge.net/)
   * [Spyc](http://github.com/mustangostang/spyc)
diff --git a/account.php b/account.php
index 295073f881172d251c46a29d5e10300b0a19204f..e8bcaf07a0270ef61c18cd300e42ada49391449f 100644
--- a/account.php
+++ b/account.php
@@ -59,35 +59,36 @@ elseif ($_POST) {
     }
 
     if (!$user_form->isValid(function($f) { return !$f->get('private'); }))
-        $errors['err'] = 'Incomplete client information';
+        $errors['err'] = __('Incomplete client information');
     elseif (!$_POST['backend'] && !$_POST['passwd1'])
-        $errors['passwd1'] = 'New password required';
+        $errors['passwd1'] = __('New password is required');
     elseif (!$_POST['backend'] && $_POST['passwd2'] != $_POST['passwd1'])
-        $errors['passwd1'] = 'Passwords do not match';
+        $errors['passwd1'] = __('Passwords do not match');
 
     // XXX: The email will always be in use already if a guest is logged in
     // and is registering for an account. Instead,
     elseif (($addr = $user_form->getField('email')->getClean())
             && ClientAccount::lookupByUsername($addr)) {
         $user_form->getField('email')->addError(
-            'Email already registered. Would you like to <a href="login.php?e='
-            .urlencode($addr).'" style="color:inherit"><strong>sign in</strong></a>?');
-        $errors['err'] = 'Unable to register account. See messages below';
+            sprintf(__('Email already registered. Would you like to %1$s sign in %2$s?'),
+            '<a href="login.php?e='.urlencode($addr).'" style="color:inherit"><strong>',
+            '</strong></a>'));
+        $errors['err'] = __('Unable to register account. See messages below');
     }
     // Users created from ClientCreateRequest
     elseif (isset($_POST['backend']) && !($user = User::fromVars($user_form->getClean())))
-        $errors['err'] = 'Unable to create local account. See messages below';
+        $errors['err'] = __('Unable to create local account. See messages below');
     // Registration for existing users
     elseif (!$user && !$thisclient && !($user = User::fromVars($user_form->getClean())))
-        $errors['err'] = 'Unable to register account. See messages below';
+        $errors['err'] = __('Unable to register account. See messages below');
     // New users and users registering from a ticket access link
     elseif (!$user && !($user = $thisclient ?: User::fromForm($user_form)))
-        $errors['err'] = 'Unable to register account. See messages below';
+        $errors['err'] = __('Unable to register account. See messages below');
     else {
         if (!($acct = ClientAccount::createForUser($user)))
-            $errors['err'] = 'Internal error. Unable to create new account';
+            $errors['err'] = __('Internal error. Unable to create new account');
         elseif (!$acct->update($_POST, $errors))
-            $errors['err'] = 'Errors configuring your profile. See messages below';
+            $errors['err'] = __('Errors configuring your profile. See messages below');
     }
 
     if (!$errors) {
diff --git a/ajax.php b/ajax.php
index 3374776c7a54ef4cbaa1c0bf7a8139d2179be2dc..bfa481a20aed7cf8c1d6134ff835a46584dcbb0e 100644
--- a/ajax.php
+++ b/ajax.php
@@ -36,7 +36,12 @@ $dispatcher = patterns('',
         url_post('^(?P<namespace>[\w.]+)$', 'createDraftClient')
     )),
     url('^/form/', patterns('ajax.forms.php:DynamicFormsAjaxAPI',
-        url_get('^help-topic/(?P<id>\d+)$', 'getClientFormsForHelpTopic')
+        url_get('^help-topic/(?P<id>\d+)$', 'getClientFormsForHelpTopic'),
+        url_post('^upload/(\d+)?$', 'upload'),
+        url_post('^upload/(\w+)?$', 'attach')
+    )),
+    url('^/i18n/(?P<lang>[\w_]+)/', patterns('ajax.i18n.php:i18nAjaxAPI',
+        url_get('(?P<tag>\w+)$', 'getLanguageFile')
     ))
 );
 Signal::send('ajax.client', $dispatcher);
diff --git a/api/cron.php b/api/cron.php
index 787460ee7c3042ee36a46ec39e8060fc73f5beaf..8f47ed3dd72e477ca6a752960ec831261b4f44fa 100644
--- a/api/cron.php
+++ b/api/cron.php
@@ -17,7 +17,7 @@
 require('api.inc.php');
 
 if (!osTicket::is_cli())
-    die('cron.php only supports local cron calls - use http -> api/tasks/cron');
+    die(__('cron.php only supports local cron calls - use http -> api/tasks/cron'));
 
 require_once(INCLUDE_DIR.'api.cron.php');
 LocalCronApiController::call();
diff --git a/api/pipe.php b/api/pipe.php
index 249cfc8a79ffef59dee8295b7feb2e8ffd94f4f2..dfaaeea7c5afcb70ca4355a2725c4d6e0383de14 100644
--- a/api/pipe.php
+++ b/api/pipe.php
@@ -20,7 +20,7 @@ require('api.inc.php');
 
 //Only local piping supported via pipe.php
 if (!osTicket::is_cli())
-    die('pipe.php only supports local piping - use http -> api/tickets.email');
+    die(__('pipe.php only supports local piping - use http -> api/tickets.email'));
 
 require_once(INCLUDE_DIR.'api.tickets.php');
 PipeApiController::process();
diff --git a/assets/default/css/theme.css b/assets/default/css/theme.css
index d68ab78e966a898a2af31a1a92f9650e5ca1ae28..e50724e19283d088aceb3acfd64cf4feb51acc8a 100644
--- a/assets/default/css/theme.css
+++ b/assets/default/css/theme.css
@@ -241,7 +241,7 @@ h2, .subject {
   display: inline-block;
   font-size: 16px;
   padding: 8px 16px 6px 16px;
-  width: 160px;
+  max-width: 220px;
   text-align: center;
   color: #fff;
   font-weight: bold;
@@ -303,14 +303,11 @@ body {
 #header #logo {
   width: 220px;
   height: 71px;
-  float: left;
 }
 #header p {
   width: 400px;
-  text-align: right;
   margin: 0;
-  padding: 10px 0;
-  float: right;
+  padding: 10px 0 0;
 }
 #nav {
   margin: 0 20px;
@@ -329,9 +326,8 @@ body {
   display: inline;
 }
 #nav li a {
-  display: block;
+  display: inline-block;
   width: auto;
-  float: left;
   height: 20px;
   line-height: 20px;
   text-align: center;
@@ -344,6 +340,14 @@ body {
   background-position: 10px 50%;
   background-repeat: no-repeat;
 }
+.rtl #nav li a {
+  background-position: right center;
+  background-position: calc(100% - 10px) center;
+  padding-left: 10px;
+  padding-right: 32px;
+  margin-right: 10px;
+  margin-left: 0;
+}
 #nav li a.active,
 #nav li a:hover {
   background-color: #dbefff;
@@ -395,20 +399,29 @@ body {
   margin: 0 auto;
   background: url('../images/poweredby.png') top left no-repeat;
 }
+.front-page-button {
+}
 #landing_page #new_ticket {
   margin-top: 40px;
+  background: url('../images/new_ticket_icon.png') top left no-repeat;
+}
+#landing_page #new_ticket,
+#landing_page #check_status,
+.front-page-button {
   width: 295px;
   padding-left: 75px;
-  float: left;
-  background: url('../images/new_ticket_icon.png') top left no-repeat;
 }
 #landing_page #check_status {
   margin-top: 40px;
-  width: 295px;
-  padding-left: 75px;
-  float: right;
   background: url('../images/check_status_icon.png') top left no-repeat;
 }
+.rtl #landing_page #new_ticket,
+.rtl #landing_page #check_status,
+.rtl .front-page-button {
+  padding-left: 0;
+  padding-right: 75px;
+  background-position: top right;
+}
 /* Landing page FAQ not yet implemented. */
 #faq {
   clear: both;
@@ -530,8 +543,6 @@ body {
 #ticketForm div label,
 #clientLogin div label {
   display: block;
-  width: 140px;
-  float: left;
 }
 label.required {
   font-weight: bold;
@@ -547,9 +558,7 @@ label.required {
   width: auto;
   border: 1px solid #aaa;
   background: #fff;
-  margin-right: 10px;
   display: block;
-  float: left;
 }
 #ticketForm div input[type=file],
 #clientLogin div input[type=file] {
@@ -635,6 +644,20 @@ label.required {
   box-shadow: inset 0 1px 2px rgba(0,0,0,0.3);
   background: url('../images/lock.png?1319655200') 95% 50% no-repeat #f6f6f6;
 }
+.rtl #clientLogin {
+    background-position: 5% 50%;
+}
+#clientLogin .instructions {
+    display:table-cell;
+    padding-left: 2em;
+    padding-right:90px;
+}
+.rtl #clientLogin .instructions {
+    padding-left: 0;
+    padding-right:0;
+    padding-right: 2em;
+    padding-left:90px;
+}
 #clientLogin p {
   clear: both;
 }
@@ -680,25 +703,6 @@ label.required {
   border: 1px solid #aaa;
   background: #fff;
 }
-#reply .attachments .uploads div {
-  display: inline-block;
-  padding-right: 20px;
-}
-#reply .file {
-  display: inline-block;
-  padding-left: 20px;
-  margin-right: 20px;
-  background: url('../images/icons/file.gif') 0 50% no-repeat;
-}
-.uploads {
-  display: inline-block;
-  padding-right: 20px;
-}
-.uploads label {
-  padding: 3px;
-  padding-right: 10px;
-  width: auto !important;
-}
 /* Ticket icons */
 .Icon {
   width: auto;
@@ -916,3 +920,18 @@ img.sign-in-image {
     width: auto;
     height: auto;
 }
+.login-box {
+    width:40%;
+    display:table-cell;
+    box-shadow: 12px 0 15px -15px rgba(0,0,0,0.4);
+    padding:15px;
+}
+.rtl .login-box {
+    box-shadow: -12px 0 15px -15px rgba(0,0,0,0.4);
+}
+.flush-right {
+    text-align: right;
+}
+.flush-left {
+    text-align: left;
+}
diff --git a/attachment.php b/attachment.php
index 73dbfd710b9a58f164034d35239842f01a102f17..05e256a42a980c0c15e5469167af2a047952718c 100644
--- a/attachment.php
+++ b/attachment.php
@@ -1,36 +1,36 @@
-<?php
-/*********************************************************************
-    attachment.php
-
-    Attachments interface for clients.
-    Clients should never see the dir paths.
-    
-    Peter Rotich <peter@osticket.com>
-    Copyright (c)  2006-2013 osTicket
-    http://www.osticket.com
-
-    Released under the GNU General Public License WITHOUT ANY WARRANTY.
-    See LICENSE.TXT for details.
-
-    vim: expandtab sw=4 ts=4 sts=4:
-**********************************************************************/
-require('secure.inc.php');
-require_once(INCLUDE_DIR.'class.attachment.php');
-//Basic checks
-if(!$thisclient
-        || !$_GET['id']
-        || !$_GET['h']
-        || !($attachment=Attachment::lookup($_GET['id']))
-        || !($file=$attachment->getFile()))
-    die('Unknown attachment!');
-
-//Validate session access hash - we want to make sure the link is FRESH! and the user has access to the parent ticket!!
-$vhash=md5($attachment->getFileId().session_id().strtolower($file->getKey()));
-if(strcasecmp(trim($_GET['h']),$vhash)
-        || !($ticket=$attachment->getTicket())
-        || !$ticket->checkUserAccess($thisclient))
-    die('Unknown or invalid attachment');
-//Download the file..
-$file->download();
-
-?>
+<?php
+/*********************************************************************
+    attachment.php
+
+    Attachments interface for clients.
+    Clients should never see the dir paths.
+
+    Peter Rotich <peter@osticket.com>
+    Copyright (c)  2006-2013 osTicket
+    http://www.osticket.com
+
+    Released under the GNU General Public License WITHOUT ANY WARRANTY.
+    See LICENSE.TXT for details.
+
+    vim: expandtab sw=4 ts=4 sts=4:
+**********************************************************************/
+require('secure.inc.php');
+require_once(INCLUDE_DIR.'class.attachment.php');
+//Basic checks
+if(!$thisclient
+        || !$_GET['id']
+        || !$_GET['h']
+        || !($attachment=Attachment::lookup($_GET['id']))
+        || !($file=$attachment->getFile()))
+    Http::response(404, __('Unknown or invalid file'));
+
+//Validate session access hash - we want to make sure the link is FRESH! and the user has access to the parent ticket!!
+$vhash=md5($attachment->getFileId().session_id().strtolower($file->getKey()));
+if(strcasecmp(trim($_GET['h']),$vhash)
+        || !($ticket=$attachment->getTicket())
+        || !$ticket->checkUserAccess($thisclient))
+    Http::response(404, __('Unknown or invalid file'));
+//Download the file..
+$file->download();
+
+?>
diff --git a/bootstrap.php b/bootstrap.php
index a4e56510c61a44e676040d6e20a54f2b89abb583..41cd7cf4f5d700772919a697182d4eb50f882f1f 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -94,9 +94,12 @@ class Bootstrap {
         define('TICKET_EVENT_TABLE',$prefix.'ticket_event');
         define('TICKET_EMAIL_INFO_TABLE',$prefix.'ticket_email_info');
         define('TICKET_COLLABORATOR_TABLE', $prefix.'ticket_collaborator');
+        define('TICKET_STATUS_TABLE', $prefix.'ticket_status');
         define('TICKET_PRIORITY_TABLE',$prefix.'ticket_priority');
+
         define('PRIORITY_TABLE',TICKET_PRIORITY_TABLE);
 
+
         define('FORM_SEC_TABLE',$prefix.'form');
         define('FORM_FIELD_TABLE',$prefix.'form_field');
 
@@ -117,6 +120,7 @@ class Bootstrap {
         define('FILTER_RULE_TABLE', $prefix.'filter_rule');
 
         define('PLUGIN_TABLE', $prefix.'plugin');
+        define('SEQUENCE_TABLE', $prefix.'sequence');
 
         define('API_KEY_TABLE',$prefix.'api_key');
         define('TIMEZONE_TABLE',$prefix.'timezone');
@@ -134,8 +138,7 @@ class Bootstrap {
             //Die gracefully on upgraded v1.6 RC5 installation - otherwise script dies with confusing message.
             if(!strcasecmp(basename($_SERVER['SCRIPT_NAME']), 'settings.php'))
                 Http::response(500,
-                    'Please rename config file include/settings.php to '
-                   .'include/ost-config.php to continue!');
+                    'Please rename config file include/settings.php to include/ost-config.php to continue!');
         } elseif(file_exists(ROOT_DIR.'setup/'))
             Http::redirect(ROOT_PATH.'setup/');
 
@@ -165,9 +168,9 @@ class Bootstrap {
             );
 
         if (!db_connect(DBHOST, DBUSER, DBPASS, $options)) {
-            $ferror='Unable to connect to the database -'.db_connect_error();
+            $ferror=sprintf('Unable to connect to the database — %s',db_connect_error());
         }elseif(!db_select_database(DBNAME)) {
-            $ferror='Unknown or invalid database '.DBNAME;
+            $ferror=sprintf('Unknown or invalid database: %s',DBNAME);
         }
 
         if($ferror) //Fatal error
@@ -176,6 +179,7 @@ class Bootstrap {
 
     function loadCode() {
         #include required files
+        require_once INCLUDE_DIR.'class.util.php';
         require(INCLUDE_DIR.'class.signal.php');
         require(INCLUDE_DIR.'class.user.php');
         require(INCLUDE_DIR.'class.auth.php');
@@ -190,6 +194,8 @@ 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';
+        require_once INCLUDE_DIR.'class.search.php';
     }
 
     function i18n_prep() {
@@ -323,8 +329,6 @@ define('THISPAGE', Misc::currentURL());
 define('DEFAULT_MAX_FILE_UPLOADS',ini_get('max_file_uploads')?ini_get('max_file_uploads'):5);
 define('DEFAULT_PRIORITY_ID',1);
 
-define('EXT_TICKET_ID_LEN',6); //Ticket create. when you start getting collisions. Applies only on random ticket ids.
-
 #Global override
 if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
     // Take the left-most item for X-Forwarded-For
diff --git a/client.inc.php b/client.inc.php
index ad4c7ee4e18c12841a46ac239fd0a7b90bd3e597..bb8e8badbe7fdce1ef104276bff36448aa4126a6 100644
--- a/client.inc.php
+++ b/client.inc.php
@@ -46,6 +46,15 @@ $msg='';
 $nav=null;
 //Make sure the user is valid..before doing anything else.
 $thisclient = UserAuthenticationBackend::getUser();
+
+if (isset($_GET['lang']) && $_GET['lang']) {
+    $_SESSION['client:lang'] = $_GET['lang'];
+}
+
+// Bootstrap gettext translations as early as possible, but after attempting
+// to sign on the agent
+TextDomain::configureForUser($thisclient);
+
 //is the user logged in?
 if($thisclient && $thisclient->getId() && $thisclient->isValid()){
      $thisclient->refreshSession();
@@ -73,7 +82,7 @@ $exempt = in_array(basename($_SERVER['SCRIPT_NAME']), array('logout.php', 'ajax.
 
 if (!$exempt && $thisclient && ($acct = $thisclient->getAccount())
         && $acct->isPasswdResetForced()) {
-    $warn = 'Password change required to continue';
+    $warn = __('Password change required to continue');
     require('profile.php'); //profile.php must request this file as require_once to avoid problems.
     exit;
 }
diff --git a/css/filedrop.css b/css/filedrop.css
new file mode 100644
index 0000000000000000000000000000000000000000..96dca339e9736b699b9249ef44c799c0584eacd7
--- /dev/null
+++ b/css/filedrop.css
@@ -0,0 +1,191 @@
+.filedrop {
+    padding-bottom: 10px;
+}
+.filedrop .dropzone {
+    border: 2px dashed rgba(0, 0, 0, 0.2);
+    padding: 8px;
+    border-radius: 5px;
+    background-color: rgba(0, 0, 0, 0.05);
+    color: #999;
+}
+.filedrop .dropzone a {
+    color: rgba(24, 78, 129, 0.7);
+}
+.filedrop .files:not(:empty) {
+    border: 1px solid rgba(0, 0, 0, 0.2);
+    border-radius: 5px 5px 0 0;
+    padding: 5px;
+}
+.filedrop .files:not(:empty) + .dropzone {
+    border-top: none;
+    border-radius: 0 0 5px 5px;
+}
+.filedrop .files .file {
+    display: block;
+    padding: 5px 10px 5px 20px;
+    margin: 0;
+    border-radius: 5px;
+}
+.rtl .filedrop .files .file {
+    padding-left: 10px;
+    padding-right: 20px;
+}
+.filedrop .files .file:hover {
+   background-color: rgba(0, 0, 0, 0.05);
+}
+.filedrop .files .file .filesize {
+  margin: 0 1em;
+  color: #999;
+}
+.filedrop .files .file .upload-rate {
+  margin: 0 10px;
+  color: #aaa;
+}
+.filedrop .files .file .trash {
+  cursor: pointer;
+}
+.filedrop .progress {
+  margin-top: 5px;
+}
+.filedrop .cancel {
+  cursor: pointer;
+}
+.filedrop .preview {
+  width: auto;
+  height: auto;
+  max-width: 60px;
+  max-height: 40px;
+  display: inline-block;
+  float: left;
+  padding-right: 10px;
+}
+.rtl .filedrop .preview {
+  padding-right: initial;
+  padding-left: 10px;
+  float: right;
+}
+.redactor_box + .filedrop .dropzone,
+.redactor_box + div > .filedrop .dropzone,
+.redactor_box + div > .filedrop .files {
+    border-top-width: 1px;
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+}
+.tooltip-preview,
+.tooltip-preview img {
+    max-width: 300px;
+    max-height: 300px;
+}
+
+/* Bootstrap 3.2 progress-bar */
+@-webkit-keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+@-o-keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+@keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+.progress {
+  height: 10px;
+  overflow: hidden;
+  background-color: #f5f5f5;
+  border-radius: 4px;
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
+          box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
+}
+.progress-bar {
+  float: left;
+  width: 0;
+  height: 100%;
+  font-size: 12px;
+  line-height: 20px;
+  color: #fff;
+  text-align: center;
+  background-color: #428bca;
+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);
+          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);
+}
+.rtl .progress-bar {
+  float: right;
+}
+.progress-bar:not(.active) {
+  -webkit-transition: width .6s ease;
+       -o-transition: width .6s ease;
+          transition: width .6s ease;
+}
+.progress-striped .progress-bar,
+.progress-bar-striped {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  -webkit-background-size: 40px 40px;
+          background-size: 40px 40px;
+}
+.progress.active .progress-bar,
+.progress-bar.active {
+  -webkit-animation: progress-bar-stripes 2s linear infinite;
+       -o-animation: progress-bar-stripes 2s linear infinite;
+          animation: progress-bar-stripes 2s linear infinite;
+}
+.progress-bar[aria-valuenow="1"],
+.progress-bar[aria-valuenow="2"] {
+  min-width: 30px;
+}
+.progress-bar[aria-valuenow="0"] {
+  min-width: 30px;
+  color: #777;
+  background-color: transparent;
+  background-image: none;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.progress-bar-success {
+  background-color: #5cb85c;
+}
+.progress-striped .progress-bar-success {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.progress-bar-info {
+  background-color: #5bc0de;
+}
+.progress-striped .progress-bar-info {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.progress-bar-warning {
+  background-color: #f0ad4e;
+}
+.progress-striped .progress-bar-warning {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+.progress-bar-danger {
+  background-color: #d9534f;
+}
+.progress-striped .progress-bar-danger {
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+}
+
diff --git a/css/flags.css b/css/flags.css
new file mode 100644
index 0000000000000000000000000000000000000000..8e67320909401edbc33f4d4495ebd4d463cb9506
--- /dev/null
+++ b/css/flags.css
@@ -0,0 +1,258 @@
+.flag {
+	width: 16px;
+	height: 11px;
+    display: inline-block;
+	background:url(../images/flags.png) no-repeat
+}
+
+.flag.flag-ad {background-position: -16px 0}
+.flag.flag-ae {background-position: -32px 0}
+.flag.flag-af {background-position: -48px 0}
+.flag.flag-ag {background-position: -64px 0}
+.flag.flag-ai {background-position: -80px 0}
+.flag.flag-al {background-position: -96px 0}
+.flag.flag-am {background-position: -112px 0}
+.flag.flag-an {background-position: -128px 0}
+.flag.flag-ao {background-position: -144px 0}
+.flag.flag-ar {background-position: -160px 0}
+.flag.flag-as {background-position: -176px 0}
+.flag.flag-at {background-position: -192px 0}
+.flag.flag-au {background-position: -208px 0}
+.flag.flag-aw {background-position: -224px 0}
+.flag.flag-az {background-position: -240px 0}
+.flag.flag-ba {background-position: 0 -11px}
+.flag.flag-bb {background-position: -16px -11px}
+.flag.flag-bd {background-position: -32px -11px}
+.flag.flag-be {background-position: -48px -11px}
+.flag.flag-bf {background-position: -64px -11px}
+.flag.flag-bg {background-position: -80px -11px}
+.flag.flag-bh {background-position: -96px -11px}
+.flag.flag-bi {background-position: -112px -11px}
+.flag.flag-bj {background-position: -128px -11px}
+.flag.flag-bm {background-position: -144px -11px}
+.flag.flag-bn {background-position: -160px -11px}
+.flag.flag-bo {background-position: -176px -11px}
+.flag.flag-br {background-position: -192px -11px}
+.flag.flag-bs {background-position: -208px -11px}
+.flag.flag-bt {background-position: -224px -11px}
+.flag.flag-bv {background-position: -240px -11px}
+.flag.flag-bw {background-position: 0 -22px}
+.flag.flag-by {background-position: -16px -22px}
+.flag.flag-bz {background-position: -32px -22px}
+.flag.flag-ca {background-position: -48px -22px}
+.flag.flag-catalonia {background-position: -64px -22px}
+.flag.flag-cd {background-position: -80px -22px}
+.flag.flag-cf {background-position: -96px -22px}
+.flag.flag-cg {background-position: -112px -22px}
+.flag.flag-ch {background-position: -128px -22px}
+.flag.flag-ci {background-position: -144px -22px}
+.flag.flag-ck {background-position: -160px -22px}
+.flag.flag-cl {background-position: -176px -22px}
+.flag.flag-cm {background-position: -192px -22px}
+.flag.flag-cn {background-position: -208px -22px}
+.flag.flag-co {background-position: -224px -22px}
+.flag.flag-cr {background-position: -240px -22px}
+.flag.flag-cu {background-position: 0 -33px}
+.flag.flag-cv {background-position: -16px -33px}
+.flag.flag-cw {background-position: -32px -33px}
+.flag.flag-cy {background-position: -48px -33px}
+.flag.flag-cz {background-position: -64px -33px}
+.flag.flag-de {background-position: -80px -33px}
+.flag.flag-dj {background-position: -96px -33px}
+.flag.flag-dk {background-position: -112px -33px}
+.flag.flag-dm {background-position: -128px -33px}
+.flag.flag-do {background-position: -144px -33px}
+.flag.flag-dz {background-position: -160px -33px}
+.flag.flag-ec {background-position: -176px -33px}
+.flag.flag-ee {background-position: -192px -33px}
+.flag.flag-eg {background-position: -208px -33px}
+.flag.flag-eh {background-position: -224px -33px}
+.flag.flag-england {background-position: -240px -33px}
+.flag.flag-er {background-position: 0 -44px}
+.flag.flag-es {background-position: -16px -44px}
+.flag.flag-et {background-position: -32px -44px}
+.flag.flag-eu {background-position: -48px -44px}
+.flag.flag-fi {background-position: -64px -44px}
+.flag.flag-fj {background-position: -80px -44px}
+.flag.flag-fk {background-position: -96px -44px}
+.flag.flag-fm {background-position: -112px -44px}
+.flag.flag-fo {background-position: -128px -44px}
+.flag.flag-fr {background-position: -144px -44px}
+.flag.flag-ga {background-position: -160px -44px}
+.flag.flag-gb {background-position: -176px -44px}
+.flag.flag-gd {background-position: -192px -44px}
+.flag.flag-ge {background-position: -208px -44px}
+.flag.flag-gf {background-position: -224px -44px}
+.flag.flag-gg {background-position: -240px -44px}
+.flag.flag-gh {background-position: 0 -55px}
+.flag.flag-gi {background-position: -16px -55px}
+.flag.flag-gl {background-position: -32px -55px}
+.flag.flag-gm {background-position: -48px -55px}
+.flag.flag-gn {background-position: -64px -55px}
+.flag.flag-gp {background-position: -80px -55px}
+.flag.flag-gq {background-position: -96px -55px}
+.flag.flag-gr {background-position: -112px -55px}
+.flag.flag-gs {background-position: -128px -55px}
+.flag.flag-gt {background-position: -144px -55px}
+.flag.flag-gu {background-position: -160px -55px}
+.flag.flag-gw {background-position: -176px -55px}
+.flag.flag-gy {background-position: -192px -55px}
+.flag.flag-hk {background-position: -208px -55px}
+.flag.flag-hm {background-position: -224px -55px}
+.flag.flag-hn {background-position: -240px -55px}
+.flag.flag-hr {background-position: 0 -66px}
+.flag.flag-ht {background-position: -16px -66px}
+.flag.flag-hu {background-position: -32px -66px}
+.flag.flag-ic {background-position: -48px -66px}
+.flag.flag-id {background-position: -64px -66px}
+.flag.flag-ie {background-position: -80px -66px}
+.flag.flag-il {background-position: -96px -66px}
+.flag.flag-im {background-position: -112px -66px}
+.flag.flag-in {background-position: -128px -66px}
+.flag.flag-io {background-position: -144px -66px}
+.flag.flag-iq {background-position: -160px -66px}
+.flag.flag-ir {background-position: -176px -66px}
+.flag.flag-is {background-position: -192px -66px}
+.flag.flag-it {background-position: -208px -66px}
+.flag.flag-je {background-position: -224px -66px}
+.flag.flag-jm {background-position: -240px -66px}
+.flag.flag-jo {background-position: 0 -77px}
+.flag.flag-jp {background-position: -16px -77px}
+.flag.flag-ke {background-position: -32px -77px}
+.flag.flag-kg {background-position: -48px -77px}
+.flag.flag-kh {background-position: -64px -77px}
+.flag.flag-ki {background-position: -80px -77px}
+.flag.flag-km {background-position: -96px -77px}
+.flag.flag-kn {background-position: -112px -77px}
+.flag.flag-kp {background-position: -128px -77px}
+.flag.flag-kr {background-position: -144px -77px}
+.flag.flag-kurdistan {background-position: -160px -77px}
+.flag.flag-kw {background-position: -176px -77px}
+.flag.flag-ky {background-position: -192px -77px}
+.flag.flag-kz {background-position: -208px -77px}
+.flag.flag-la {background-position: -224px -77px}
+.flag.flag-lb {background-position: -240px -77px}
+.flag.flag-lc {background-position: 0 -88px}
+.flag.flag-li {background-position: -16px -88px}
+.flag.flag-lk {background-position: -32px -88px}
+.flag.flag-lr {background-position: -48px -88px}
+.flag.flag-ls {background-position: -64px -88px}
+.flag.flag-lt {background-position: -80px -88px}
+.flag.flag-lu {background-position: -96px -88px}
+.flag.flag-lv {background-position: -112px -88px}
+.flag.flag-ly {background-position: -128px -88px}
+.flag.flag-ma {background-position: -144px -88px}
+.flag.flag-mc {background-position: -160px -88px}
+.flag.flag-md {background-position: -176px -88px}
+.flag.flag-me {background-position: -192px -88px}
+.flag.flag-mg {background-position: -208px -88px}
+.flag.flag-mh {background-position: -224px -88px}
+.flag.flag-mk {background-position: -240px -88px}
+.flag.flag-ml {background-position: 0 -99px}
+.flag.flag-mm {background-position: -16px -99px}
+.flag.flag-mn {background-position: -32px -99px}
+.flag.flag-mo {background-position: -48px -99px}
+.flag.flag-mp {background-position: -64px -99px}
+.flag.flag-mq {background-position: -80px -99px}
+.flag.flag-mr {background-position: -96px -99px}
+.flag.flag-ms {background-position: -112px -99px}
+.flag.flag-mt {background-position: -128px -99px}
+.flag.flag-mu {background-position: -144px -99px}
+.flag.flag-mv {background-position: -160px -99px}
+.flag.flag-mw {background-position: -176px -99px}
+.flag.flag-mx {background-position: -192px -99px}
+.flag.flag-my {background-position: -208px -99px}
+.flag.flag-mz {background-position: -224px -99px}
+.flag.flag-na {background-position: -240px -99px}
+.flag.flag-nc {background-position: 0 -110px}
+.flag.flag-ne {background-position: -16px -110px}
+.flag.flag-nf {background-position: -32px -110px}
+.flag.flag-ng {background-position: -48px -110px}
+.flag.flag-ni {background-position: -64px -110px}
+.flag.flag-nl {background-position: -80px -110px}
+.flag.flag-no {background-position: -96px -110px}
+.flag.flag-np {background-position: -112px -110px}
+.flag.flag-nr {background-position: -128px -110px}
+.flag.flag-nu {background-position: -144px -110px}
+.flag.flag-nz {background-position: -160px -110px}
+.flag.flag-om {background-position: -176px -110px}
+.flag.flag-pa {background-position: -192px -110px}
+.flag.flag-pe {background-position: -208px -110px}
+.flag.flag-pf {background-position: -224px -110px}
+.flag.flag-pg {background-position: -240px -110px}
+.flag.flag-ph {background-position: 0 -121px}
+.flag.flag-pk {background-position: -16px -121px}
+.flag.flag-pl {background-position: -32px -121px}
+.flag.flag-pm {background-position: -48px -121px}
+.flag.flag-pn {background-position: -64px -121px}
+.flag.flag-pr {background-position: -80px -121px}
+.flag.flag-ps {background-position: -96px -121px}
+.flag.flag-pt {background-position: -112px -121px}
+.flag.flag-pw {background-position: -128px -121px}
+.flag.flag-py {background-position: -144px -121px}
+.flag.flag-qa {background-position: -160px -121px}
+.flag.flag-re {background-position: -176px -121px}
+.flag.flag-ro {background-position: -192px -121px}
+.flag.flag-rs {background-position: -208px -121px}
+.flag.flag-ru {background-position: -224px -121px}
+.flag.flag-rw {background-position: -240px -121px}
+.flag.flag-sa {background-position: 0 -132px}
+.flag.flag-sb {background-position: -16px -132px}
+.flag.flag-sc {background-position: -32px -132px}
+.flag.flag-scotland {background-position: -48px -132px}
+.flag.flag-sd {background-position: -64px -132px}
+.flag.flag-se {background-position: -80px -132px}
+.flag.flag-sg {background-position: -96px -132px}
+.flag.flag-sh {background-position: -112px -132px}
+.flag.flag-si {background-position: -128px -132px}
+.flag.flag-sk {background-position: -144px -132px}
+.flag.flag-sl {background-position: -160px -132px}
+.flag.flag-sm {background-position: -176px -132px}
+.flag.flag-sn {background-position: -192px -132px}
+.flag.flag-so {background-position: -208px -132px}
+.flag.flag-somaliland {background-position: -224px -132px}
+.flag.flag-sr {background-position: -240px -132px}
+.flag.flag-ss {background-position: 0 -143px}
+.flag.flag-st {background-position: -16px -143px}
+.flag.flag-sv {background-position: -32px -143px}
+.flag.flag-sx {background-position: -48px -143px}
+.flag.flag-sy {background-position: -64px -143px}
+.flag.flag-sz {background-position: -80px -143px}
+.flag.flag-tc {background-position: -96px -143px}
+.flag.flag-td {background-position: -112px -143px}
+.flag.flag-tf {background-position: -128px -143px}
+.flag.flag-tg {background-position: -144px -143px}
+.flag.flag-th {background-position: -160px -143px}
+.flag.flag-tj {background-position: -176px -143px}
+.flag.flag-tk {background-position: -192px -143px}
+.flag.flag-tl {background-position: -208px -143px}
+.flag.flag-tm {background-position: -224px -143px}
+.flag.flag-tn {background-position: -240px -143px}
+.flag.flag-to {background-position: 0 -154px}
+.flag.flag-tr {background-position: -16px -154px}
+.flag.flag-tt {background-position: -32px -154px}
+.flag.flag-tv {background-position: -48px -154px}
+.flag.flag-tw {background-position: -64px -154px}
+.flag.flag-tz {background-position: -80px -154px}
+.flag.flag-ua {background-position: -96px -154px}
+.flag.flag-ug {background-position: -112px -154px}
+.flag.flag-um {background-position: -128px -154px}
+.flag.flag-us {background-position: -144px -154px}
+.flag.flag-uy {background-position: -160px -154px}
+.flag.flag-uz {background-position: -176px -154px}
+.flag.flag-va {background-position: -192px -154px}
+.flag.flag-vc {background-position: -208px -154px}
+.flag.flag-ve {background-position: -224px -154px}
+.flag.flag-vg {background-position: -240px -154px}
+.flag.flag-vi {background-position: 0 -165px}
+.flag.flag-vn {background-position: -16px -165px}
+.flag.flag-vu {background-position: -32px -165px}
+.flag.flag-wales {background-position: -48px -165px}
+.flag.flag-wf {background-position: -64px -165px}
+.flag.flag-ws {background-position: -80px -165px}
+.flag.flag-ye {background-position: -96px -165px}
+.flag.flag-yt {background-position: -112px -165px}
+.flag.flag-za {background-position: -128px -165px}
+.flag.flag-zanzibar {background-position: -144px -165px}
+.flag.flag-zm {background-position: -160px -165px}
+.flag.flag-zw {background-position: -176px -165px}
diff --git a/css/osticket.css b/css/osticket.css
index 37cdb26f2846ac6df03cc266bc7b32f0a2785cd6..0d6d887324cc95c93ba4c7ec896d932e512a183d 100644
--- a/css/osticket.css
+++ b/css/osticket.css
@@ -77,6 +77,9 @@ input.dp {
     border-radius: 3px;
     text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
     line-height: 14px;
+    position: absolute;
+    top: 3em;
+    right: 0.5em;
 }
 
 .delete-draft:hover {
diff --git a/css/redactor.css b/css/redactor.css
index cd84755209f82d0407b68f368cbd8978c48346a8..e6123e23e60353cf93e5282574538fee093b7fec 100644
--- a/css/redactor.css
+++ b/css/redactor.css
@@ -187,7 +187,6 @@ body .redactor_box_fullscreen {
 .redactor_editor img {
   height: auto;
 }
-.redactor_editor div,
 .redactor_editor p,
 .redactor_editor ul,
 .redactor_editor ol,
@@ -207,10 +206,14 @@ body .redactor_box_fullscreen {
   margin-bottom: 15px !important;
 }
 .redactor_editor blockquote {
-  margin-left: 1.5em !important;
-  padding-left: 0 !important;
   color: #777;
+  padding: 10px 20px;
   font-style: italic !important;
+  border-left: 5px solid #eeeeee;
+}
+[dir="rtl"] .redactor_editor blockquote {
+  border-left: none;
+  border-right: 5px solid #eeeeee;
 }
 .redactor_editor ul,
 .redactor_editor ol {
diff --git a/css/rtl.css b/css/rtl.css
new file mode 100644
index 0000000000000000000000000000000000000000..b66cbe61a1c7ca17d6ef3cdd7ec5181bcbd7cd44
--- /dev/null
+++ b/css/rtl.css
@@ -0,0 +1,143 @@
+.rtl {
+    direction: rtl;
+    unicode-bidi: embed;
+}
+.rtl .pull-left {
+    float: right;
+}
+.rtl .pull-right {
+    float: left;
+}
+.rtl table.list thead th a {
+    background-position: 0% 50%;
+    padding-right: 3px;
+    padding-left: 15px;
+}
+.rtl table.list thead th,
+.rtl table.list caption,
+.rtl .dialog th,
+.rtl .tip_box th {
+    text-align: right;
+}
+.rtl .dialog h3 {
+    padding-right: inherit;
+    padding-left: 3em;
+}
+.rtl .dialog a.close {
+    right: auto;
+    left: 1em;
+}
+.rtl #nav .inactive li {
+    text-align: right;
+}
+.rtl #nav .inactive li a {
+    background-position: 100% 50%;
+    padding-left: 0;
+    padding-right: 24px;
+}
+.rtl #nav li.inactive > ul {
+    left: auto;
+    right: -1px;
+}
+.rtl .tip_close {
+    right: auto;
+    left: 0.5em;
+}
+.rtl .tip_content h1 {
+    padding-right: 0;
+    padding-left: 1.5em;
+}
+.rtl #msg_notice,
+.rtl #warning_bar,
+.rtl #msg_warning,
+.rtl #msg_error,
+.rtl .error-banner {
+    background-position: 99% 50%;
+    background-position: calc(100% - 10px) 50%;
+    padding-left: 10px;
+    padding-right: 36px;
+}
+.rtl .form_table th, .rtl div.section-break {
+    text-align: right;
+}
+.rtl .flush-right {
+    text-align: left;
+}
+.rtl .flush-left {
+    text-align: right;
+}
+.rtl .draft-saved {
+    right: initial;
+    left: 0.5em;
+}
+.rtl #sequences .manage-buttons {
+    margin-right: initial;
+    margin-left: 60px;
+}
+.rtl .row-item .button-group {
+    right: initial;
+    left: 0;
+}
+.rtl .row-item .button-group div {
+    padding-left: 9px;
+    padding-right: 12px;
+}
+.rtl .row-item .delete {
+    border-left: none;
+    border-right: 1px solid rgba(0,0,0,0.7);
+}
+.rtl [class^="icon-"].pull-left, [class*=" icon-"].pull-left {
+    margin-right: 0;
+    margin-left: 0.3em;
+}
+.rtl ul.tabs {
+    padding-left: 4px;
+    padding-right: 20px;
+    text-align:right;
+}
+.rtl #response_options ul.tabs {
+    padding-right:190px;
+    padding-left: 4px;
+}
+.rtl .action-button i.icon-caret-down {
+    border-left: none;
+    border-right: 1px solid #aaa;
+    margin-left: 0;
+    margin-right: 5px;
+    padding-left: 0;
+    padding-right: 5px;
+}
+.rtl .action-dropdown ul {
+    text-align: right;
+}
+.rtl .file {
+    padding-left: initial;
+    padding-right: 20px;
+    margin-right: initial;
+    margin-left: 20px;
+    background: url(../scp/images/icons/file.gif) 100% 50% no-repeat;
+}
+.rtl .floating-options {
+    right: auto;
+    left: 0;
+    padding-right: initial;
+    padding-left: 5px;
+}
+.rtl .quicknote .header .header-right {
+    right: auto;
+    left: 1em;
+}
+.rtl .quicknote .header .options {
+    border-right: 1px solid rgba(0,0,0,0.2);
+    border-left: none;
+    padding-right: 10px;
+    padding-left: initial;
+    margin-right: 5px;
+    margin-left: initial;
+}
+.rtl i.note-type {
+    border-left: 1px solid rgba(0, 0, 0, 0.2);
+    border-right: none;
+    padding-left: 8px;
+    padding-right: initial;
+}
diff --git a/css/thread.css b/css/thread.css
index 049165780c799beed80f45e1e4fc8f1f24a0cb65..cdf25a7200d7b0365283e88a2fe6760c66c86fa7 100644
--- a/css/thread.css
+++ b/css/thread.css
@@ -394,7 +394,7 @@
 .thread-body blockquote,
 .thread-body pre {
 	font-size: 14px;
-	line-height: 1.25rem;
+	line-height: 1.4rem;
 }
 
 /* Adjust plain/text messages posted as <pre> in the thread body to show in
diff --git a/image.php b/image.php
index 405c87c9f6de2df262738aded389343189285331..5b7f27283ba23175ff12772f4212a41859ea3e18 100644
--- a/image.php
+++ b/image.php
@@ -25,7 +25,7 @@ $h=trim($_GET['h']);
 if(!$h  || strlen($h)!=64  //32*2
         || !($file=AttachmentFile::lookup(substr($h,0,32))) //first 32 is the file hash.
         || strcasecmp($h, $file->getDownloadHash())) //next 32 is file id + session hash.
-    Http::response(404, 'Unknown or invalid file');
+    Http::response(404, __('Unknown or invalid file'));
 
 $file->display();
 ?>
diff --git a/images/flags.png b/images/flags.png
new file mode 100644
index 0000000000000000000000000000000000000000..7b3a380895c1f5d236075fc64f506a5352fb2350
Binary files /dev/null and b/images/flags.png differ
diff --git a/include/Spyc.php b/include/Spyc.php
index 17d67db3ea32baa729606efc7e42b6468b0d8131..1c0fc31a8c90db69aeaefe4291590fe3c4aea96d 100644
--- a/include/Spyc.php
+++ b/include/Spyc.php
@@ -863,9 +863,12 @@ class Spyc {
   }
 
   function startsLiteralBlock ($line) {
-    $lastChar = substr (trim($line), -1);
-    if ($lastChar != '>' && $lastChar != '|') return false;
-    if ($lastChar == '|') return $lastChar;
+    $matches = array();
+    if (!preg_match('`(>|\|)[\d+-]?$`', $line, $matches))
+        return false;
+
+    $lastChar = $matches[1];
+
     // HTML tags should not be counted as literal blocks.
     if (preg_match ('#<.*?>$#', $line)) return false;
     return $lastChar;
diff --git a/include/ajax.config.php b/include/ajax.config.php
index 119264c35b8d0164e3b9be2200f78de046ceaf97..733fa014d2c18af5e09d92c5b3ba497561f1b8ca 100644
--- a/include/ajax.config.php
+++ b/include/ajax.config.php
@@ -22,12 +22,22 @@ class ConfigAjaxAPI extends AjaxController {
     function scp() {
         global $cfg;
 
+        $lang = Internationalization::getCurrentLanguage();
+        list($sl, $locale) = explode('_', $lang);
+
+        $rtl = false;
+        foreach (Internationalization::availableLanguages() as $info) {
+            if (isset($info['direction']))
+                $rtl = true;
+        }
+
         $config=array(
               'lock_time'       => ($cfg->getLockTime()*3600),
-              'max_file_uploads'=> (int) $cfg->getStaffMaxFileUploads(),
               'html_thread'     => (bool) $cfg->isHtmlThreadEnabled(),
               'date_format'     => ($cfg->getDateFormat()),
-              'allow_attachments' => (bool) $cfg->allowAttachments(),
+              'lang'            => $lang,
+              'short_lang'      => $sl,
+              'has_rtl'         => $rtl,
         );
         return $this->json_encode($config);
     }
@@ -35,16 +45,25 @@ class ConfigAjaxAPI extends AjaxController {
     function client() {
         global $cfg;
 
+        $lang = Internationalization::getCurrentLanguage();
+        list($sl, $locale) = explode('_', $lang);
+
+        $rtl = false;
+        foreach (Internationalization::availableLanguages() as $info) {
+            if (isset($info['direction']))
+                $rtl = true;
+        }
+
         $config=array(
-            'allow_attachments' => (bool) $cfg->allowOnlineAttachments(),
-            'file_types'      => $cfg->getAllowedFileTypes(),
-            'max_file_size'   => (int) $cfg->getMaxFileSize(),
-            'max_file_uploads'=> (int) $cfg->getClientMaxFileUploads(),
             'html_thread'     => (bool) $cfg->isHtmlThreadEnabled(),
+            'lang'            => $lang,
+            'short_lang'      => $sl,
+            'has_rtl'         => $rtl,
         );
 
         $config = $this->json_encode($config);
         Http::cacheable(md5($config), $cfg->lastModified());
+        header('Content-Type: application/json; charset=UTF-8');
 
         return $config;
     }
diff --git a/include/ajax.content.php b/include/ajax.content.php
index 3126c28e2fa34c479c62a69e07e3da8f311bc12a..d7863cb880c48e781059e4db60049341bd48154e 100644
--- a/include/ajax.content.php
+++ b/include/ajax.content.php
@@ -24,13 +24,16 @@ class ContentAjaxAPI extends AjaxController {
             $content=sprintf('<div
                     style="width:500px;">&nbsp;<strong>%s</strong><br><p
                     style="white-space:pre-line;">%s</p>
-                    <hr><strong>Log Date:</strong> <em>%s</em> <strong>IP Address:</strong> <em>%s</em></div>',
+                    <hr><strong>%s:</strong> <em>%s</em> <strong>%s:</strong> <em>%s</em></div>',
                     $log->getTitle(),
                     Format::display(str_replace(',',', ',$log->getText())),
+                    __('Log Date'),
                     Format::db_daydatetime($log->getCreateDate()),
+                    __('IP Address'),
                     $log->getIP());
         }else {
-            $content='<div style="width:295px;">&nbsp;<strong>Error:</strong>Unknown or invalid log ID</div>';
+            $content='<div style="width:295px;">&nbsp;<strong>'.__('Error').':</strong>'.
+                sprintf(__('%s: Unknown or invalid ID.'), __('log entry')).'</div>';
         }
 
         return $content;
@@ -40,60 +43,60 @@ class ContentAjaxAPI extends AjaxController {
 
         $content='
 <div style="width:680px;">
-    <h2>Ticket Variables</h2>
-    Please note that non-base variables depend on the context of use. Visit osTicket Wiki for up-to-date documentation.
+    <h2>'.__('Ticket Variables').'</h2>
+    '.__('Please note that non-base variables depend on the context of use. Visit osTicket Wiki for up to date documentation.').'
     <br/>
     <table width="100%" border="0" cellspacing=1 cellpadding=2>
-        <tr><td width="55%" valign="top"><b>Base Variables</b></td><td><b>Other Variables</b></td></tr>
+        <tr><td width="55%" valign="top"><b>'.__('Base Variables').'</b></td><td><b>'.__('Other Variables').'</b></td></tr>
         <tr>
             <td width="55%" valign="top">
                 <table width="100%" border="0" cellspacing=1 cellpadding=1>
-                    <tr><td width="130">%{ticket.id}</td><td>Ticket ID (internal ID)</td></tr>
-                    <tr><td>%{ticket.number}</td><td>Ticket number (external ID)</td></tr>
-                    <tr><td>%{ticket.email}</td><td>Email address</td></tr>
-                    <tr><td>%{ticket.name}</td><td>Full name &mdash;
-                        <em>see name expansion</em></td></tr>
-                    <tr><td>%{ticket.subject}</td><td>Subject</td></tr>
-                    <tr><td>%{ticket.phone}</td><td>Phone number | ext</td></tr>
-                    <tr><td>%{ticket.status}</td><td>Status</td></tr>
-                    <tr><td>%{ticket.priority}</td><td>Priority</td></tr>
-                    <tr><td>%{ticket.assigned}</td><td>Assigned staff and/or team</td></tr>
-                    <tr><td>%{ticket.create_date}</td><td>Date created</td></tr>
-                    <tr><td>%{ticket.due_date}</td><td>Due date</td></tr>
-                    <tr><td>%{ticket.close_date}</td><td>Date closed</td></tr>
-                    <tr><td>%{ticket.auth_token}</td><td>Auth. token used for auto-login</td></tr>
-                    <tr><td>%{ticket.client_link}</td><td>Client\'s ticket view link</td></tr>
-                    <tr><td>%{ticket.staff_link}</td><td>Staff\'s ticket view link</td></tr>
-                    <tr><td colspan="2" style="padding:5px 0 5px 0;"><em>Expandable Variables (See Wiki)</em></td></tr>
-                    <tr><td>%{ticket.<b>topic</b>}</td><td>Help topic</td></tr>
-                    <tr><td>%{ticket.<b>dept</b>}</td><td>Department</td></tr>
-                    <tr><td>%{ticket.<b>staff</b>}</td><td>Assigned/closing staff</td></tr>
-                    <tr><td>%{ticket.<b>team</b>}</td><td>Assigned/closing team</td></tr>
+                    <tr><td width="130">%{ticket.id}</td><td>'.__('Ticket ID').' ('.__('internal ID').')</td></tr>
+                    <tr><td>%{ticket.number}</td><td>'.__('Ticket number').' ('.__('external ID').')</td></tr>
+                    <tr><td>%{ticket.email}</td><td>'.__('Email address').'</td></tr>
+                    <tr><td>%{ticket.name}</td><td>'.__('Full name').' &mdash;
+                        <em>'.__('see name expansion').'</em></td></tr>
+                    <tr><td>%{ticket.subject}</td><td>'.__('Subject').'</td></tr>
+                    <tr><td>%{ticket.phone}</td><td>'.__('Phone number | ext').'</td></tr>
+                    <tr><td>%{ticket.status}</td><td>'.__('Status').'</td></tr>
+                    <tr><td>%{ticket.priority}</td><td>'.__('Priority').'</td></tr>
+                    <tr><td>%{ticket.assigned}</td><td>'.__('Assigned agent and/or team').'</td></tr>
+                    <tr><td>%{ticket.create_date}</td><td>'.__('Date created').'</td></tr>
+                    <tr><td>%{ticket.due_date}</td><td>'.__('Due date').'</td></tr>
+                    <tr><td>%{ticket.close_date}</td><td>'.__('Date closed').'</td></tr>
+                    <tr><td>%{recipient.ticket_link}</td><td>'.__('Auth. token used for auto-login').'</td></tr>
+                    <tr><td>%{ticket.client_link}</td><td>'.__('Client\'s ticket view link').'</td></tr>
+                    <tr><td>%{recipient.ticket_link}</td><td>'.__('Agent\'s ticket view link').'</td></tr>
+                    <tr><td colspan="2" style="padding:5px 0 5px 0;"><em>'.__('Expandable Variables (See Wiki)').'</em></td></tr>
+                    <tr><td>%{ticket.<b>topic</b>}</td><td>'.__('Help topic').'</td></tr>
+                    <tr><td>%{ticket.<b>dept</b>}</td><td>'.__('Department').'</td></tr>
+                    <tr><td>%{ticket.<b>staff</b>}</td><td>'.__('Assigned/closing agent').'</td></tr>
+                    <tr><td>%{ticket.<b>team</b>}</td><td>'.__('Assigned/closing team').'</td></tr>
                 </table>
             </td>
             <td valign="top">
                 <table width="100%" border="0" cellspacing=1 cellpadding=1>
-                    <tr><td width="100">%{message}</td><td>Incoming message</td></tr>
-                    <tr><td>%{response}</td><td>Outgoing response</td></tr>
-                    <tr><td>%{comments}</td><td>Assign/transfer comments</td></tr>
-                    <tr><td>%{note}</td><td>Internal note <em>(expandable)</em></td></tr>
-                    <tr><td>%{assignee}</td><td>Assigned staff/team</td></tr>
-                    <tr><td>%{assigner}</td><td>Staff assigning the ticket</td></tr>
-                    <tr><td>%{url}</td><td>osTicket\'s base url (FQDN)</td></tr>
+                    <tr><td width="100">%{message}</td><td>'.__('Incoming message').'</td></tr>
+                    <tr><td>%{response}</td><td>'.__('Outgoing response').'</td></tr>
+                    <tr><td>%{comments}</td><td>'.__('Assign/transfer comments').'</td></tr>
+                    <tr><td>%{note}</td><td>'.__('Internal note <em>(expandable)</em>').'</td></tr>
+                    <tr><td>%{assignee}</td><td>'.__('Assigned agent/team').'</td></tr>
+                    <tr><td>%{assigner}</td><td>'.__('Agent assigning the ticket').'</td></tr>
+                    <tr><td>%{url}</td><td>'.__('osTicket\'s base url (FQDN)').'</td></tr>
                     <tr><td>%{reset_link}</td>
-                        <td>Reset link used by the password reset feature</td></tr>
+                        <td>'.__('Reset link used by the password reset feature').'</td></tr>
                 </table>
                 <table width="100%" border="0" cellspacing=1 cellpadding=1>
-                    <tr><td colspan="2"><b>Name Expansion</b></td></tr>
-                    <tr><td>.first</td><td>First Name</td></tr>
-                    <tr><td>.middle</td><td>Middle Name(s)</td></tr>
-                    <tr><td>.last</td><td>Last Name</td></tr>
-                    <tr><td>.full</td><td>First Last</td></tr>
-                    <tr><td>.legal</td><td>First M. Last</td></tr>
-                    <tr><td>.short</td><td>First L.</td></tr>
-                    <tr><td>.formal</td><td>Mr. Last</td></tr>
-                    <tr><td>.shortformal</td><td>F. Last</td></tr>
-                    <tr><td>.lastfirst</td><td>Last, First</td></tr>
+                    <tr><td colspan="2"><b>'.__('Name Expansion').'</b></td></tr>
+                    <tr><td>.first</td><td>'.__('First Name').'</td></tr>
+                    <tr><td>.middle</td><td>'.__('Middle Name(s)').'</td></tr>
+                    <tr><td>.last</td><td>'.__('Last Name').'</td></tr>
+                    <tr><td>.full</td><td>'.__('First Last').'</td></tr>
+                    <tr><td>.legal</td><td>'.__('First M. Last').'</td></tr>
+                    <tr><td>.short</td><td>'.__('First L.').'</td></tr>
+                    <tr><td>.formal</td><td>'.__('Mr. Last').'</td></tr>
+                    <tr><td>.shortformal</td><td>'.__('F. Last').'</td></tr>
+                    <tr><td>.lastfirst</td><td>'.__('Last, First').'</td></tr>
                 </table>
             </td>
         </tr>
diff --git a/include/ajax.draft.php b/include/ajax.draft.php
index 34679a7e8d6075a40ac24ee29c7581d382caa69a..41fde2be24ff955b793f1fbf773b9d8372616c0b 100644
--- a/include/ajax.draft.php
+++ b/include/ajax.draft.php
@@ -67,6 +67,8 @@ class DraftAjaxAPI extends AjaxController {
     }
 
     function _uploadInlineImage($draft) {
+        global $cfg;
+
         if (!isset($_POST['data']) && !isset($_FILES['file']))
             Http::response(422, "File not included properly");
 
@@ -76,9 +78,26 @@ class DraftAjaxAPI extends AjaxController {
                 $_FILES['image'][$k] = array($v);
             unset($_FILES['file']);
 
-            $file = AttachmentFile::format($_FILES['image'], true);
+            $file = AttachmentFile::format($_FILES['image']);
             # TODO: Detect unacceptable attachment extension
             # TODO: Verify content-type and check file-content to ensure image
+            $type = $file[0]['type'];
+            if (strpos($file[0]['type'], 'image/') !== 0)
+                return Http::response(403,
+                    JsonDataEncoder::encode(array(
+                        'error' => 'File type is not allowed',
+                    ))
+                );
+
+            # TODO: Verify file size is acceptable
+            if ($file[0]['size'] > $cfg->getMaxFileSize())
+                return Http::response(403,
+                    JsonDataEncoder::encode(array(
+                        'error' => 'File is too large',
+                    ))
+                );
+
+
             if (!($ids = $draft->attachments->upload($file))) {
                 if ($file[0]['error']) {
                     return Http::response(403,
@@ -265,11 +284,11 @@ class DraftAjaxAPI extends AjaxController {
 
         $files = array();
         $folders = array(
-            'C' => 'Canned Responses',
-            'F' => 'FAQ Articles',
-            'T' => 'Email Templates',
-            'L' => 'Logos',
-            'P' => 'Pages',
+            'C' => __('Canned Responses'),
+            'F' => __('FAQ Articles'),
+            'T' => __('Email Templates'),
+            'L' => __('Logos'),
+            'P' => __('Pages'),
         );
         while (list($id, $type) = db_fetch_row($res)) {
             $f = AttachmentFile::lookup($id);
diff --git a/include/ajax.forms.php b/include/ajax.forms.php
index 9a026e83c5861f5621678dcf0864f008efd44e3a..e53b91348a4d47b3a0b0bc44b001051f292f22d4 100644
--- a/include/ajax.forms.php
+++ b/include/ajax.forms.php
@@ -2,6 +2,7 @@
 
 require_once(INCLUDE_DIR . 'class.topic.php');
 require_once(INCLUDE_DIR . 'class.dynamic_forms.php');
+require_once(INCLUDE_DIR . 'class.forms.php');
 
 class DynamicFormsAjaxAPI extends AjaxController {
     function getForm($form_id) {
@@ -23,8 +24,18 @@ class DynamicFormsAjaxAPI extends AjaxController {
             $_SESSION[':form-data'] = array_merge($_SESSION[':form-data'], $_GET);
         }
 
-        if ($form = $topic->getForm())
+        if ($form = $topic->getForm()) {
+            ob_start();
             $form->getForm($_SESSION[':form-data'])->render(!$client);
+            $html = ob_get_clean();
+            ob_start();
+            print $form->getMedia();
+            $media = ob_get_clean();
+        }
+        return $this->encode(array(
+            'media' => $media,
+            'html' => $html,
+        ));
     }
 
     function getClientFormsForHelpTopic($topic_id) {
@@ -38,10 +49,13 @@ class DynamicFormsAjaxAPI extends AjaxController {
 
     function saveFieldConfiguration($field_id) {
         $field = DynamicFormField::lookup($field_id);
-        if (!$field->setConfiguration())
-            include(STAFFINC_DIR . 'templates/dynamic-field-config.tmpl.php');
+        if (!$field->setConfiguration()) {
+            include STAFFINC_DIR . 'templates/dynamic-field-config.tmpl.php';
+            return;
+        }
         else
             $field->save();
+        Http::response(201, 'Field successfully updated');
     }
 
     function deleteAnswer($entry_id, $field_id) {
@@ -58,21 +72,49 @@ class DynamicFormsAjaxAPI extends AjaxController {
         $ent->delete();
     }
 
-    function getListItemProperties($item_id) {
-        if (!($item = DynamicListItem::lookup($item_id)))
+    function getListItemProperties($list_id, $item_id) {
+
+        $list = DynamicList::lookup($list_id);
+        if (!$list || !($item = $list->getItem( (int) $item_id)))
             Http::response(404, 'No such list item');
 
         include(STAFFINC_DIR . 'templates/list-item-properties.tmpl.php');
     }
 
-    function saveListItemProperties($item_id) {
-        if (!($item = DynamicListItem::lookup($item_id)))
+    function saveListItemProperties($list_id, $item_id) {
+
+        $list = DynamicList::lookup($list_id);
+        if (!$list || !($item = $list->getItem( (int) $item_id)))
             Http::response(404, 'No such list item');
 
-        if (!$item->setConfiguration())
-            include(STAFFINC_DIR . 'templates/list-item-properties.tmpl.php');
+        if (!$item->setConfiguration()) {
+            include STAFFINC_DIR . 'templates/list-item-properties.tmpl.php';
+            return;
+        }
         else
             $item->save();
+
+        Http::response(201, 'Successfully updated record');
+    }
+
+    function upload($id) {
+        if (!$field = DynamicFormField::lookup($id))
+            Http::response(400, 'No such field');
+
+        $impl = $field->getImpl();
+        if (!$impl instanceof FileUploadField)
+            Http::response(400, 'Upload to a non file-field');
+
+        return JsonDataEncoder::encode(
+            array('id'=>$impl->ajaxUpload())
+        );
+    }
+
+    function attach() {
+        $field = new FileUploadField();
+        return JsonDataEncoder::encode(
+            array('id'=>$field->ajaxUpload(true))
+        );
     }
 }
 ?>
diff --git a/include/ajax.i18n.php b/include/ajax.i18n.php
new file mode 100644
index 0000000000000000000000000000000000000000..3aeaefa135b8448db40e97fc8fb2b0ce1a13ac6e
--- /dev/null
+++ b/include/ajax.i18n.php
@@ -0,0 +1,41 @@
+<?php
+/*********************************************************************
+    ajax.i18n.php
+
+    Callbacks to get internaltionalized pieces for osticket
+
+    Peter Rotich <peter@osticket.com>
+    Jared Hancock <jared@osticket.com>
+    Copyright (c)  2006-2014 osTicket
+    http://www.osticket.com
+
+    Released under the GNU General Public License WITHOUT ANY WARRANTY.
+    See LICENSE.TXT for details.
+
+    vim: expandtab sw=4 ts=4 sts=4:
+**********************************************************************/
+
+if(!defined('INCLUDE_DIR')) die('!');
+
+class i18nAjaxAPI extends AjaxController {
+    function getLanguageFile($lang, $key) {
+        global $cfg;
+
+        $i18n = new Internationalization($lang);
+        switch ($key) {
+        case 'js':
+            $data = $i18n->getTemplate('js/redactor.js')->getRawData();
+            $data .= $i18n->getTemplate('js/jquery.ui.datepicker.js')->getRawData();
+            // Strings from various javascript files
+            $data .= $i18n->getTemplate('js/osticket-strings.js')->getRawData();
+            header('Content-Type: text/javascript; charset=UTF-8');
+            break;
+        default:
+            Http::response(404, 'No such i18n data');
+        }
+
+        Http::cacheable(md5($data), $cfg->lastModified());
+        echo $data;
+    }
+}
+?>
diff --git a/include/ajax.kbase.php b/include/ajax.kbase.php
index 2f5bc75bfa6e0a8c88c9ab7ab2b4a0961224eb23..16e961ad94b8b2afad49e677c38c1af330edbb66 100644
--- a/include/ajax.kbase.php
+++ b/include/ajax.kbase.php
@@ -45,9 +45,9 @@ class KbaseAjaxAPI extends AjaxController {
                 '<div style="width:650px;">
                  <strong>%s</strong><div class="thread-body">%s</div>
                  <div class="clear"></div>
-                 <div class="faded">Last updated %s</div>
+                 <div class="faded">'.__('Last updated %s').'</div>
                  <hr>
-                 <a href="faq.php?id=%d">View</a> | <a href="faq.php?id=%d">Attachments (%s)</a>',
+                 <a href="faq.php?id=%d">'.__('View').'</a> | <a href="faq.php?id=%d">'.__('Attachments (%d)').'</a>',
                 $faq->getQuestion(),
                 $faq->getAnswerWithImages(),
                 Format::db_daydatetime($faq->getUpdateDate()),
@@ -55,7 +55,7 @@ class KbaseAjaxAPI extends AjaxController {
                 $faq->getId(),
                 $faq->getNumAttachments());
         if($thisstaff && $thisstaff->canManageFAQ()) {
-            $resp.=sprintf(' | <a href="faq.php?id=%d&a=edit">Edit</a>',$faq->getId());
+            $resp.=sprintf(' | <a href="faq.php?id=%d&a=edit">'.__('Edit').'</a>',$faq->getId());
 
         }
         $resp.='</div>';
diff --git a/include/ajax.orgs.php b/include/ajax.orgs.php
index 7737f57c11fd6780efc82602379a4734f8dc88a9..393c6dde48ba1d0a797bf34447d12ce28c526962 100644
--- a/include/ajax.orgs.php
+++ b/include/ajax.orgs.php
@@ -57,7 +57,7 @@ class OrgsAjaxAPI extends AjaxController {
             Http::response(404, 'Unknown organization');
 
         $info = array(
-            'title' => sprintf('Update %s', $org->getName())
+            'title' => sprintf(__('Update %s'), $org->getName())
         );
 
         $forms = $org->getForms();
@@ -119,7 +119,7 @@ class OrgsAjaxAPI extends AjaxController {
             Http::response(404, 'Unknown organization');
 
         $info = array();
-        $info['title'] = 'Add User';
+        $info['title'] = __('Add User');
         $info['action'] = '#orgs/'.$org->getId().'/add-user';
         $info['onselect'] = 'ajax.php/orgs/'.$org->getId().'/add-user/';
 
@@ -130,20 +130,20 @@ class OrgsAjaxAPI extends AjaxController {
         if ($_POST) {
             if ($_POST['id']) { //Existing useer
                 if (!($user = User::lookup($_POST['id'])))
-                    $info['error'] = 'Unknown user selected';
+                    $info['error'] = __('Unknown user selected');
                 elseif ($user->getOrgId() == $org->getId())
                     $info['error'] = sprintf('%s already belongs to the organization',
                             Format::htmlchars($user->getName()));
             } else { //Creating new  user
                 $form = UserForm::getUserForm()->getForm($_POST);
                 if (!($user = User::fromForm($form)))
-                    $info['error'] = 'Error adding user - try again!';
+                    $info['error'] = __('Error adding user - try again!');
             }
 
             if (!$info['error'] && $user && $user->setOrganization($org))
                 Http::response(201, $user->to_json());
             elseif (!$info['error'])
-                $info['error'] = 'Unable to add user to the organization - try again';
+                $info['error'] = __('Unable to add user to the organization - try again');
 
         } elseif ($remote && $userId) {
             list($bk, $userId) = explode(':', $userId, 2);
@@ -157,9 +157,9 @@ class OrgsAjaxAPI extends AjaxController {
 
         if ($user && $user->getOrgId()) {
             if ($user->getOrgId() == $org->getId())
-                $info['warn'] = 'User already belongs to this organization!';
+                $info['warn'] = __('User already belongs to this organization!');
             else
-                $info['warn'] = "Are you sure you want to change the user's organization?";
+                $info['warn'] = __("Are you sure you want to change the user's organization?");
         }
 
         ob_start();
@@ -178,7 +178,7 @@ class OrgsAjaxAPI extends AjaxController {
             Http::response(404, 'No such organization');
 
         $info = array(
-            'title' => 'Import Users',
+            'title' => __('Import Users'),
             'action' => "#orgs/$org_id/import-users",
             'upload_url' => "orgs.php?a=import-users",
         );
@@ -204,10 +204,10 @@ class OrgsAjaxAPI extends AjaxController {
             if (($org = Organization::fromForm($form)))
                 Http::response(201, $org->to_json());
 
-            $info = array('error' =>'Error adding organization - try again!');
+            $info = array('error' =>__('Error adding organization - try again!'));
         }
 
-        $info['title'] = 'Add New Organization';
+        $info['title'] = __('Add New Organization');
         $info['search'] = false;
 
         return self::_lookupform($form, $info);
@@ -221,7 +221,7 @@ class OrgsAjaxAPI extends AjaxController {
 
         if ($id) $org = Organization::lookup($id);
 
-        $info = array('title' => 'Select Organization');
+        $info = array('title' => __('Select Organization'));
 
         ob_start();
         include(STAFFINC_DIR . 'templates/org-lookup.tmpl.php');
@@ -243,7 +243,7 @@ class OrgsAjaxAPI extends AjaxController {
     static function _lookupform($form=null, $info=array()) {
 
         if (!$info or !$info['title'])
-            $info += array('title' => 'Organization Lookup');
+            $info += array('title' => __('Organization Lookup'));
 
         ob_start();
         include(STAFFINC_DIR . 'templates/org-lookup.tmpl.php');
diff --git a/include/ajax.reports.php b/include/ajax.reports.php
index 33512dd8936cb8926967c06f8c2ed379be568dc2..e9e660a8c69b36f0c876ca63724b8735789aae94 100644
--- a/include/ajax.reports.php
+++ b/include/ajax.reports.php
@@ -28,10 +28,10 @@ include_once(INCLUDE_DIR.'class.ticket.php');
  */
 class OverviewReportAjaxAPI extends AjaxController {
     function enumTabularGroups() {
-        return $this->encode(array("dept"=>"Department", "topic"=>"Topics",
+        return $this->encode(array("dept"=>__("Department"), "topic"=>__("Topics"),
             # XXX: This will be relative to permissions based on the
             # logged-in-staff. For basic staff, this will be 'My Stats'
-            "staff"=>"Staff"));
+            "staff"=>__("Agent")));
     }
 
     function getData() {
@@ -45,7 +45,7 @@ class OverviewReportAjaxAPI extends AjaxController {
                 "pk" => "dept_id",
                 "sort" => 'T1.dept_name',
                 "fields" => 'T1.dept_name',
-                "headers" => array('Department'),
+                "headers" => array(__('Department')),
                 "filter" => ('T1.dept_id IN ('.implode(',', db_input($thisstaff->getDepts())).')')
             ),
             "topic" => array(
@@ -55,7 +55,7 @@ class OverviewReportAjaxAPI extends AjaxController {
                 "fields" => "CONCAT_WS(' / ',"
                     ."(SELECT P.topic FROM ".TOPIC_TABLE." P WHERE P.topic_id = T1.topic_pid),"
                     ."T1.topic) as name ",
-                "headers" => array('Help Topic'),
+                "headers" => array(__('Help Topic')),
                 "filter" => '1'
             ),
             "staff" => array(
@@ -63,7 +63,7 @@ class OverviewReportAjaxAPI extends AjaxController {
                 "pk" => 'staff_id',
                 "sort" => 'name',
                 "fields" => "CONCAT_WS(' ', T1.firstname, T1.lastname) as name",
-                "headers" => array('Staff Member'),
+                "headers" => array(__('Agent')),
                 "filter" =>
                     ('T1.staff_id=S1.staff_id
                       AND
@@ -143,8 +143,8 @@ class OverviewReportAjaxAPI extends AjaxController {
                     $r[] = null;
         }
         return array("columns" => array_merge($info['headers'],
-                        array('Opened','Assigned','Overdue','Closed','Reopened',
-                              'Service Time','Response Time')),
+                        array(__('Opened'),__('Assigned'),__('Overdue'),__('Closed'),__('Reopened'),
+                              __('Service Time'),__('Response Time'))),
                      "data" => $rows);
     }
 
@@ -158,7 +158,7 @@ class OverviewReportAjaxAPI extends AjaxController {
         foreach ($data['data'] as $row)
             $csv .= "\n" . '"' . implode('","', $row) . '"';
         Http::download(
-            sprintf('%s-report.csv', $this->get('group', 'Department')),
+            sprintf('%s-report.csv', $this->get('group', __('Department'))),
             'text/csv', $csv);
     }
 
diff --git a/include/ajax.sequence.php b/include/ajax.sequence.php
new file mode 100644
index 0000000000000000000000000000000000000000..37be03269c87483a77e8d99cc7aaf840ffaeae34
--- /dev/null
+++ b/include/ajax.sequence.php
@@ -0,0 +1,106 @@
+<?php
+
+require_once(INCLUDE_DIR . 'class.sequence.php');
+
+class SequenceAjaxAPI extends AjaxController {
+
+    /**
+     * Ajax: GET /sequence/<id>
+     *
+     * Fetches the current value of a sequence
+     *
+     * Get-Arguments:
+     * format - (string) format string used to format the current value of
+     *      the sequence.
+     *
+     * Returns:
+     * (string) Current sequence number, optionally formatted
+     *
+     * Throws:
+     * 403 - Not logged in
+     * 404 - Unknown sequence id
+     * 422 - Invalid sequence id
+     */
+    function current($id) {
+        global $thisstaff;
+
+        if (!$thisstaff)
+            Http::response(403, 'Login required');
+        elseif ($id == 0)
+            $sequence = new RandomSequence();
+        elseif (!$id || !is_numeric($id))
+            Http::response(422, 'Id is required');
+        elseif (!($sequence = Sequence::lookup($id)))
+            Http::response(404, 'No such object');
+
+        return $sequence->current($_GET['format']);
+    }
+
+    /**
+     * Ajax: GET|POST /sequence/manage
+     *
+     * Gets a dialog box content or updates data from the content
+     *
+     * Post-Arguments:
+     * seq[<id>][*] - Updated information for existing sequences
+     * seq[<new-*>[*] - Information for new sequences
+     * seq[<id>][deleted] - If set to true, indicates that the sequence
+     *      should be deleted from the database
+     *
+     * Throws:
+     * 403 - Not logged in
+     * 422 - Information sent for update of unknown sequence
+     */
+    function manage() {
+        global $thisstaff;
+
+        if (!$thisstaff)
+            Http::response(403, 'Login required');
+
+        $sequences = Sequence::objects()->all();
+        $info = array(
+            'action' => '#sequence/manage',
+        );
+
+        $valid = true;
+        if ($_POST) {
+            foreach ($_POST['seq'] as $id=>$info) {
+                if (strpos($id, 'new-') === 0) {
+                    unset($info['id']);
+                    $sequences[] = Sequence::create($info);
+                }
+                else {
+                    foreach ($sequences as $s) {
+                        if ($s->id == $id)
+                            break;
+                        $s = false;
+                    }
+                    if (!$s) {
+                        Http::response(422, $id . ': Invalid or unknown sequence');
+                    }
+                    elseif ($info['deleted']) {
+                        $s->delete();
+                        continue;
+                    }
+                    foreach ($info as $f=>$val) {
+                        if (isset($s->{$f}))
+                            $s->set($f, $val);
+                        elseif ($f == 'current')
+                            $s->next = $val;
+                    }
+                    if (($v = $s->isValid()) !== true) {
+                        $msg = sprintf('%s: %s', $s->getName(), $valid);
+                        $valid = false;
+                    }
+                }
+            }
+            if ($valid) {
+                foreach ($sequences as $s)
+                    $s->save();
+                Http::response(205, 'All sequences updated');
+            }
+        }
+
+        include STAFFINC_DIR . 'templates/sequence-manage.tmpl.php';
+    }
+}
diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index a1e30c43b327c4ac67bc07a956c7f88d2de9fd41..708bb6dbdc88b3f6adadba7205cc60efb5da8b4c 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -98,18 +98,22 @@ class TicketsAjaxAPI extends AjaxController {
     }
 
     function _search($req) {
-        global $thisstaff, $cfg;
+        global $thisstaff, $cfg, $ost;
 
         $result=array();
+        $criteria = array();
+
         $select = 'SELECT ticket.ticket_id';
-        $from = ' FROM '.TICKET_TABLE.' ticket ';
+        $from = ' FROM '.TICKET_TABLE.' ticket
+                  LEFT JOIN '.TICKET_STATUS_TABLE.' status
+                    ON (status.id = ticket.status_id) ';
         //Access control.
         $where = ' WHERE ( (ticket.staff_id='.db_input($thisstaff->getId())
-                    .' AND ticket.status="open" )';
+                    .' AND status.state="open" )';
 
         if(($teams=$thisstaff->getTeams()) && count(array_filter($teams)))
             $where.=' OR (ticket.team_id IN ('.implode(',', db_input(array_filter($teams)))
-                   .' ) AND ticket.status="open")';
+                   .' ) AND status.state="open" )';
 
         if(!$thisstaff->showAssignedOnly() && ($depts=$thisstaff->getDepts()))
             $where.=' OR ticket.dept_id IN ('.implode(',', db_input($depts)).')';
@@ -117,51 +121,71 @@ class TicketsAjaxAPI extends AjaxController {
         $where.=' ) ';
 
         //Department
-        if($req['deptId'])
+        if ($req['deptId']) {
             $where.=' AND ticket.dept_id='.db_input($req['deptId']);
+            $criteria['dept_id'] = $req['deptId'];
+        }
 
         //Help topic
-        if($req['topicId'])
+        if($req['topicId']) {
             $where.=' AND ticket.topic_id='.db_input($req['topicId']);
+            $criteria['topic_id'] = $req['topicId'];
+        }
 
-        //Status
-        switch(strtolower($req['status'])) {
-            case 'open':
-                $where.=' AND ticket.status="open" ';
-                break;
-            case 'answered':
-                $where.=' AND ticket.status="open" AND ticket.isanswered=1 ';
-                break;
-            case 'overdue':
-                $where.=' AND ticket.status="open" AND ticket.isoverdue=1 ';
-                break;
-            case 'closed':
-                $where.=' AND ticket.status="closed" ';
-                break;
+        // Status
+        if ($req['statusId']
+                && ($status=TicketStatus::lookup($req['statusId']))) {
+            $where .= sprintf(' AND status.id="%d" ',
+                    $status->getId());
+            $criteria['status_id'] = $status->getId();
+        }
+
+        // Flags
+        if ($req['flag']) {
+            switch (strtolower($req['flag'])) {
+                case 'answered':
+                    $where .= ' AND ticket.isanswered =1 ';
+                    $criteria['isanswered'] = 1;
+                    $criteria['state'] = 'open';
+                    $where .= ' AND status.state="open" ';
+                    break;
+                case 'overdue':
+                    $where .= ' AND ticket.isoverdue =1 ';
+                    $criteria['isoverdue'] = 1;
+                    $criteria['state'] = 'open';
+                    $where .= ' AND status.state="open" ';
+                    break;
+            }
         }
 
         //Assignee
-        if(isset($req['assignee']) && strcasecmp($req['status'], 'closed'))  {
+        if($req['assignee'] && strcasecmp($req['status'], 'closed'))  { # assigned-to
             $id=preg_replace("/[^0-9]/", "", $req['assignee']);
             $assignee = $req['assignee'];
-            $where.= ' AND ( ( ticket.status="open" ';
-            if($assignee[0]=='t')
+            $where.= ' AND ( ( status.state="open" ';
+            if($assignee[0]=='t') {
                 $where.=' AND ticket.team_id='.db_input($id);
-            elseif($assignee[0]=='s')
-                $where.=' AND ticket.staff_id='.db_input($id);
-            elseif(is_numeric($id))
+                $criteria['team_id'] = $id;
+            }
+            elseif($assignee[0]=='s' || is_numeric($id)) {
                 $where.=' AND ticket.staff_id='.db_input($id);
+                $criteria['staff_id'] = $id;
+            }
 
             $where.=')';
 
             if($req['staffId'] && !$req['status']) //Assigned TO + Closed By
-                $where.= ' OR (ticket.staff_id='.db_input($req['staffId']). ' AND ticket.status="closed") ';
-            elseif(isset($req['staffId'])) // closed by any
-                $where.= ' OR ticket.status="closed" ';
+                $where.= ' OR (ticket.staff_id='.db_input($req['staffId']).
+                    ' AND status.state IN("closed")) ';
+            elseif($req['staffId']) // closed by any
+                $where.= ' OR status.state IN("closed") ';
 
             $where.= ' ) ';
-        } elseif($req['staffId']) {
-            $where.=' AND (ticket.staff_id='.db_input($req['staffId']).' AND ticket.status="closed") ';
+        } elseif($req['staffId']) { # closed-by
+            $where.=' AND (ticket.staff_id='.db_input($req['staffId']).' AND
+                status.state IN("closed")) ';
+            $criteria['state__in'] = array('closed');
+            $criteria['staff_id'] = $req['staffId'];
         }
 
         //dates
@@ -170,40 +194,14 @@ class TicketsAjaxAPI extends AjaxController {
         if( ($startTime && $startTime>time()) or ($startTime>$endTime && $endTime>0))
             $startTime=$endTime=0;
 
-        if($startTime)
+        if($startTime) {
             $where.=' AND ticket.created>=FROM_UNIXTIME('.$startTime.')';
+            $criteria['created__gte'] = $startTime;
+        }
 
-        if($endTime)
+        if($endTime) {
             $where.=' AND ticket.created<=FROM_UNIXTIME('.$endTime.')';
-
-        //Query
-        $joins = array();
-        if($req['query']) {
-            $queryterm=db_real_escape($req['query'], false);
-
-            // Setup sets of joins and queries
-            $joins[] = array(
-                'from' =>
-                    'LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON (ticket.ticket_id=thread.ticket_id )',
-                'where' => "thread.title LIKE '%$queryterm%' OR thread.body LIKE '%$queryterm%'"
-            );
-            $joins[] = array(
-                'from' =>
-                    'LEFT JOIN '.FORM_ENTRY_TABLE.' tentry ON (tentry.object_id = ticket.ticket_id AND tentry.object_type="T")
-                    LEFT JOIN '.FORM_ANSWER_TABLE.' tans ON (tans.entry_id = tentry.id AND tans.value_id IS NULL)',
-                'where' => "tans.value LIKE '%$queryterm%'"
-            );
-            $joins[] = array(
-                'from' =>
-                   'LEFT JOIN '.FORM_ENTRY_TABLE.' uentry ON (uentry.object_id = ticket.user_id
-                   AND uentry.object_type="U")
-                   LEFT JOIN '.FORM_ANSWER_TABLE.' uans ON (uans.entry_id = uentry.id
-                   AND uans.value_id IS NULL)
-                   LEFT JOIN '.USER_TABLE.' user ON (ticket.user_id = user.id)
-                   LEFT JOIN '.USER_EMAIL_TABLE.' uemail ON (user.id = uemail.user_id)',
-                'where' =>
-                    "uemail.address LIKE '%$queryterm%' OR user.name LIKE '%$queryterm%' OR uans.value LIKE '%$queryterm%'",
-            );
+            $criteria['created__lte'] = $startTime;
         }
 
         // Dynamic fields
@@ -213,10 +211,18 @@ class TicketsAjaxAPI extends AjaxController {
                     && ($val = $req[$f->getFormName()])) {
                 $name = $f->get('name') ? $f->get('name')
                     : 'field_'.$f->get('id');
-                if ($f->getImpl()->hasIdValue() && is_numeric($val))
-                    $cwhere = "cdata.`{$name}_id` = ".db_input($val);
-                else
+                if (is_array($val)) {
+                    $cwhere = '(' . implode(' OR ', array_map(
+                        function($k) use ($name) {
+                            return sprintf('FIND_IN_SET(%s, `%s`)', db_input($k), $name);
+                        }, $val)
+                    ) . ')';
+                    $criteria["cdata.{$name}"] = $val;
+                }
+                else {
                     $cwhere = "cdata.`$name` LIKE '%".db_real_escape($val)."%'";
+                    $criteria["cdata.{$name}"] = $val;
+                }
                 $where .= ' AND ('.$cwhere.')';
                 $cdata_search = true;
             }
@@ -225,6 +231,14 @@ class TicketsAjaxAPI extends AjaxController {
             $from .= 'LEFT JOIN '.TABLE_PREFIX.'ticket__cdata '
                     ." cdata ON (cdata.ticket_id = ticket.ticket_id)";
 
+        //Query
+        $joins = array();
+        if($req['query']) {
+            // Setup sets of joins and queries
+            if ($s = $ost->searcher)
+               return $s->find($req['query'], $criteria, 'Ticket');
+        }
+
         $sections = array();
         foreach ($joins as $j) {
             $sections[] = "$select $from {$j['from']} $where AND ({$j['where']})";
@@ -250,13 +264,12 @@ class TicketsAjaxAPI extends AjaxController {
         if (count($tickets)) {
             $uid = md5($_SERVER['QUERY_STRING']);
             $_SESSION["adv_$uid"] = $tickets;
-            $result['success'] =sprintf(
-                "Search criteria matched %d %s - <a href='tickets.php?%s'>view</a>",
-                count($tickets), (count($tickets)>1?"tickets":"ticket"),
-                'advsid='.$uid
-            );
+            $result['success'] = sprintf(__("Search criteria matched %s"),
+                    sprintf(_N('%d ticket', '%d tickets', count($tickets)), count($tickets)
+                ))
+                . " - <a href='tickets.php?advsid=$uid'>".__('view')."</a>";
         } else {
-            $result['fail']='No tickets found matching your search criteria.';
+            $result['fail']=__('No tickets found matching your search criteria.');
         }
 
         return $this->json_encode($result);
@@ -269,14 +282,14 @@ class TicketsAjaxAPI extends AjaxController {
             return 0;
 
         if(!($ticket = Ticket::lookup($tid)) || !$ticket->checkStaffAccess($thisstaff))
-            return $this->json_encode(array('id'=>0, 'retry'=>false, 'msg'=>'Lock denied!'));
+            return $this->json_encode(array('id'=>0, 'retry'=>false, 'msg'=>__('Lock denied!')));
 
         //is the ticket already locked?
         if($ticket->isLocked() && ($lock=$ticket->getLock()) && !$lock->isExpired()) {
             /*Note: Ticket->acquireLock does the same logic...but we need it here since we need to know who owns the lock up front*/
             //Ticket is locked by someone else.??
             if($lock->getStaffId()!=$thisstaff->getId())
-                return $this->json_encode(array('id'=>0, 'retry'=>false, 'msg'=>'Unable to acquire lock.'));
+                return $this->json_encode(array('id'=>0, 'retry'=>false, 'msg'=>__('Unable to acquire lock.')));
 
             //Ticket already locked by staff...try renewing it.
             $lock->renew(); //New clock baby!
@@ -333,14 +346,9 @@ class TicketsAjaxAPI extends AjaxController {
         global $thisstaff;
 
         if(!$thisstaff || !($ticket=Ticket::lookup($tid)) || !$ticket->checkStaffAccess($thisstaff))
-            Http::response(404, 'No such ticket');
+            Http::response(404, __('No such ticket'));
 
-        ob_start();
         include STAFFINC_DIR . 'templates/ticket-preview.tmpl.php';
-        $resp = ob_get_contents();
-        ob_end_clean();
-
-        return $resp;
     }
 
     function addRemoteCollaborator($tid, $bk, $id) {
@@ -358,7 +366,7 @@ class TicketsAjaxAPI extends AjaxController {
         $form = UserForm::getUserForm()->getForm($user_info);
         $info = array();
         if (!$user_info)
-            $info['error'] = 'Unable to find user in directory';
+            $info['error'] = __('Unable to find user in directory');
 
         return self::_addcollaborator($ticket, null, $form, $info);
     }
@@ -369,7 +377,7 @@ class TicketsAjaxAPI extends AjaxController {
 
         if (!($ticket=Ticket::lookup($tid))
                 || !$ticket->checkStaffAccess($thisstaff))
-            Http::response(404, 'No such ticket');
+            Http::response(404, __('No such ticket'));
 
 
         $user = $uid? User::lookup($uid) : null;
@@ -389,15 +397,15 @@ class TicketsAjaxAPI extends AjaxController {
         $errors = $info = array();
         if ($user) {
             if ($user->getId() == $ticket->getOwnerId())
-                $errors['err'] = sprintf('Ticket owner, %s, is a collaborator by default!',
+                $errors['err'] = sprintf(__('Ticket owner, %s, is a collaborator by default!'),
                         Format::htmlchars($user->getName()));
             elseif (($c=$ticket->addCollaborator($user,
                             array('isactive'=>1), $errors))) {
-                $note = Format::htmlchars(sprintf('%s <%s> added as a collaborator',
+                $note = Format::htmlchars(sprintf(__('%s <%s> added as a collaborator'),
                             Format::htmlchars($c->getName()), $c->getEmail()));
-                $ticket->logNote('New Collaborator Added', $note,
+                $ticket->logNote(__('New Collaborator Added'), $note,
                     $thisstaff, false);
-                $info = array('msg' => sprintf('%s added as a collaborator',
+                $info = array('msg' => sprintf(__('%s added as a collaborator'),
                             Format::htmlchars($c->getName())));
                 return self::_collaborators($ticket, $info);
             }
@@ -406,7 +414,7 @@ class TicketsAjaxAPI extends AjaxController {
         if($errors && $errors['err']) {
             $info +=array('error' => $errors['err']);
         } else {
-            $info +=array('error' =>'Unable to add collaborator - try again');
+            $info +=array('error' =>__('Unable to add collaborator. Internal error'));
         }
 
         return self::_addcollaborator($ticket, $user, $form, $info);
@@ -474,7 +482,7 @@ class TicketsAjaxAPI extends AjaxController {
     function _addcollaborator($ticket, $user=null, $form=null, $info=array()) {
 
         $info += array(
-                    'title' => sprintf('Ticket #%s: Add a collaborator', $ticket->getNumber()),
+                    'title' => sprintf(__('Ticket #%s: Add a collaborator'), $ticket->getNumber()),
                     'action' => sprintf('#tickets/%d/add-collaborator', $ticket->getId()),
                     'onselect' => sprintf('ajax.php/tickets/%d/add-collaborator/', $ticket->getId()),
                     );
@@ -541,7 +549,7 @@ class TicketsAjaxAPI extends AjaxController {
 
 
         $info = array(
-            'title' => sprintf('Ticket #%s: %s', $ticket->getNumber(),
+            'title' => sprintf(__('Ticket #%s: %s'), $ticket->getNumber(),
                 Format::htmlchars($user->getName()))
             );
 
@@ -570,7 +578,7 @@ class TicketsAjaxAPI extends AjaxController {
         $forms = $user->getForms();
 
         $info = array(
-            'title' => sprintf('Ticket #%s: %s', $ticket->getNumber(),
+            'title' => sprintf(__('Ticket #%s: %s'), $ticket->getNumber(),
                 Format::htmlchars($user->getName()))
             );
 
@@ -593,7 +601,7 @@ class TicketsAjaxAPI extends AjaxController {
         $user = User::lookup($ticket->getOwnerId());
 
         $info = array(
-                'title' => sprintf('Change user for ticket #%s', $ticket->getNumber())
+                'title' => sprintf(__('Change user for ticket #%s'), $ticket->getNumber())
                 );
 
         return self::_userlookup($user, null, $info);
@@ -695,5 +703,329 @@ class TicketsAjaxAPI extends AjaxController {
 
         return $canned->getFormattedResponse($format, $varReplacer);
     }
+
+    function changeTicketStatus($tid, $status, $id=0) {
+        global $thisstaff;
+
+        if (!$thisstaff)
+            Http::response(403, 'Access denied');
+        elseif (!$tid
+                || !($ticket=Ticket::lookup($tid))
+                || !$ticket->checkStaffAccess($thisstaff))
+            Http::response(404, 'Unknown ticket #');
+
+        $info = array();
+        $state = null;
+        switch($status) {
+            case 'open':
+            case 'reopen':
+                $state = 'open';
+                break;
+            case 'close':
+                if (!$thisstaff->canCloseTickets())
+                    Http::response(403, 'Access denied');
+                $state = 'closed';
+                break;
+            case 'delete':
+                if (!$thisstaff->canDeleteTickets())
+                    Http::response(403, 'Access denied');
+                $state = 'deleted';
+                break;
+            default:
+                $state = $ticket->getStatus()->getState();
+                $info['warn'] = sprintf('%s %s',
+                        __('Unknown or invalid'), __('status'));
+        }
+
+        $info['status_id'] = $id ?: $ticket->getStatusId();
+
+        return self::_changeTicketStatus($ticket, $state, $info);
+    }
+
+    function setTicketStatus($tid) {
+        global $thisstaff, $ost;
+
+        if (!$thisstaff)
+            Http::response(403, 'Access denied');
+        elseif (!$tid
+                || !($ticket=Ticket::lookup($tid))
+                || !$ticket->checkStaffAccess($thisstaff))
+            Http::response(404, 'Unknown ticket #');
+
+        $errors = $info = array();
+        if (!$_POST['status_id']
+                || !($status= TicketStatus::lookup($_POST['status_id'])))
+            $errors['status_id'] = sprintf('%s %s',
+                    __('Unknown or invalid'), __('status'));
+        elseif ($status->getId() == $ticket->getStatusId())
+            $errors['err'] = sprintf(__('Ticket already set to %s status'),
+                    __($status->getName()));
+        else {
+            // Make sure the agent has permission to set the status
+            switch(mb_strtolower($status->getState())) {
+                case 'open':
+                    if (!$thisstaff->canCloseTickets()
+                            && !$thisstaff->canCreateTickets())
+                        $errors['err'] = sprintf(__('You do not have permission %s.'),
+                                __('to reopen tickets'));
+                    break;
+                case 'closed':
+                    if (!$thisstaff->canCloseTickets())
+                        $errors['err'] = sprintf(__('You do not have permission %s.'),
+                                __('to resolve/close tickets'));
+                    break;
+                case 'deleted':
+                    if (!$thisstaff->canDeleteTickets())
+                        $errors['err'] = sprintf(__('You do not have permission %s.'),
+                                __('to archive/delete tickets'));
+                    break;
+                default:
+                    $errors['err'] = sprintf('%s %s',
+                            __('Unknown or invalid'), __('status'));
+            }
+        }
+
+        $state = strtolower($status->getState());
+
+        if (!$errors && $ticket->setStatus($status, $_REQUEST['comments'])) {
+
+            if ($state == 'deleted') {
+                $msg = sprintf('%s %s',
+                        sprintf(__('Ticket #%s'), $ticket->getNumber()),
+                        __('deleted sucessfully')
+                        );
+            } elseif ($state != 'open') {
+                 $msg = sprintf(__('%s status changed to %s'),
+                         sprintf(__('Ticket #%s'), $ticket->getNumber()),
+                         $status->getName());
+            } else {
+                $msg = sprintf(
+                        __('%s status changed to %s'),
+                        __('Ticket'),
+                        $status->getName());
+            }
+
+            $_SESSION['::sysmsgs']['msg'] = $msg;
+
+            Http::response(201, 'Successfully processed');
+        } elseif (!$errors['err']) {
+            $errors['err'] =  __('Error updating ticket status');
+        }
+
+        $state = $state ?: $ticket->getStatus()->getState();
+        $info['status_id'] = $status
+            ? $status->getId() : $ticket->getStatusId();
+
+        return self::_changeTicketStatus($ticket, $state, $info, $errors);
+    }
+
+    function changeSelectedTicketsStatus($status, $id=0) {
+        global $thisstaff, $cfg;
+
+        if (!$thisstaff)
+            Http::response(403, 'Access denied');
+
+        $state = null;
+        $info = array();
+        switch($status) {
+            case 'open':
+            case 'reopen':
+                $state = 'open';
+                break;
+            case 'close':
+                if (!$thisstaff->canCloseTickets())
+                    Http::response(403, 'Access denied');
+                $state = 'closed';
+                break;
+            case 'delete':
+                if (!$thisstaff->canDeleteTickets())
+                    Http::response(403, 'Access denied');
+
+                $state = 'deleted';
+                break;
+            default:
+                $info['warn'] = sprintf('%s %s',
+                        __('Unknown or invalid'), __('status'));
+        }
+
+        $info['status_id'] = $id;
+
+        return self::_changeSelectedTicketsStatus($state, $info);
+    }
+
+    function setSelectedTicketsStatus($state) {
+        global $thisstaff, $ost;
+
+        $errors = $info = array();
+        if (!$thisstaff || !$thisstaff->canManageTickets())
+            $errors['err'] = sprintf('%s %s',
+                    sprintf(__('You do not have permission %s.'),
+                        __('to mass manage tickets')),
+                    __('Contact admin for such access'));
+        elseif (!$_REQUEST['tids'] || !count($_REQUEST['tids']))
+            $errors['err']=sprintf(__('You must select at least %s.'),
+                    __('one ticket'));
+        elseif (!($status= TicketStatus::lookup($_REQUEST['status_id'])))
+            $errors['status_id'] = sprintf('%s %s',
+                    __('Unknown or invalid'), __('status'));
+        elseif (!$errors) {
+            // Make sure the agent has permission to set the status
+            switch(mb_strtolower($status->getState())) {
+                case 'open':
+                    if (!$thisstaff->canCloseTickets()
+                            && !$thisstaff->canCreateTickets())
+                        $errors['err'] = sprintf(__('You do not have permission %s.'),
+                                __('to reopen tickets'));
+                    break;
+                case 'closed':
+                    if (!$thisstaff->canCloseTickets())
+                        $errors['err'] = sprintf(__('You do not have permission %s.'),
+                                __('to resolve/close tickets'));
+                    break;
+                case 'deleted':
+                    if (!$thisstaff->canDeleteTickets())
+                        $errors['err'] = sprintf(__('You do not have permission %s.'),
+                                __('to archive/delete tickets'));
+                    break;
+                default:
+                    $errors['err'] = sprintf('%s %s',
+                            __('Unknown or invalid'), __('status'));
+            }
+        }
+
+        $count = count($_REQUEST['tids']);
+        if (!$errors) {
+            $i = 0;
+            $comments = $_REQUEST['comments'];
+            foreach ($_REQUEST['tids'] as $tid) {
+                if (($ticket=Ticket::lookup($tid))
+                        && $ticket->getStatusId() != $status->getId()
+                        && $ticket->checkStaffAccess($thisstaff)
+                        && $ticket->setStatus($status, $comments))
+                    $i++;
+            }
+
+            if (!$i)
+                $errors['err'] = sprintf(__('Unable to change status for %s'),
+                        _N('the selected ticket', 'any of the selected tickets', $count));
+            else {
+                // Assume success
+                if ($i==$count) {
+
+                    if (!strcasecmp($status->getState(), 'deleted')) {
+                        $msg = sprintf(__( 'Successfully deleted %s.'),
+                                _N('selected ticket', 'selected tickets',
+                                    $count));
+                    } else {
+                       $msg = sprintf(
+                            __(
+                                /* 1$ will be 'selected ticket(s)', 2$ is the new status */
+                                'Successfully changed status of %1$s to %2$s'),
+                            _N('selected ticket', 'selected tickets',
+                                $count),
+                            $status->getName());
+                    }
+
+                    $_SESSION['::sysmsgs']['msg'] = $msg;
+                } else {
+
+                    if (!strcasecmp($status->getState(), 'deleted')) {
+                        $warn = sprintf(__('Successfully deleted %s.'),
+                                sprintf(__('%1$d of %2$d selected tickets'),
+                                    $i, $count)
+                                );
+                    } else {
+
+                        $warn = sprintf(
+                                __('%1$d of %2$d %3$s status changed to %4$s'),$i, $count,
+                                _N('selected ticket', 'selected tickets',
+                                    $count),
+                                $status->getName());
+                    }
+
+                    $_SESSION['::sysmsgs']['warn'] = $warn;
+                }
+
+                Http::response(201, 'Successfully processed');
+            }
+        }
+
+        return self::_changeSelectedTicketsStatus($state, $info, $errors);
+    }
+
+    private function _changeSelectedTicketsStatus($state, $info=array(), $errors=array()) {
+
+        $count = $_REQUEST['count'] ?:
+            ($_REQUEST['tids'] ?  count($_REQUEST['tids']) : 0);
+
+        $info['title'] = sprintf(__('%1$s Tickets &mdash; %2$d selected'),
+                TicketStateField::getVerb($state),
+                 $count);
+
+        if (!strcasecmp($state, 'deleted')) {
+
+            $info['warn'] = sprintf(__(
+                        'Are you sure you want to DELETE %s?'),
+                    _N('selected ticket', 'selected tickets', $count)
+                    );
+
+            $info['extra'] = sprintf('<strong>%s</strong>', __(
+                        'Deleted tickets CANNOT be recovered, including any associated attachments.')
+                    );
+
+            $info['placeholder'] = sprintf(__(
+                        'Optional reason for deleting %s'),
+                    _N('selected ticket', 'selected tickets', $count));
+        }
+
+        $info['status_id'] = $info['status_id'] ?: $_REQUEST['status_id'];
+        $info['comments'] = Format::htmlchars($_REQUEST['comments']);
+
+        return self::_changeStatus($state, $info, $errors);
+    }
+
+    private function _changeTicketStatus($ticket, $state, $info=array(), $errors=array()) {
+
+        $verb = TicketStateField::getVerb($state);
+
+        $info['action'] = sprintf('#tickets/%d/status', $ticket->getId());
+        $info['title'] = sprintf(__(
+                    /* 1$ will be a verb, like 'open', 2$ will be the ticket number */
+                    '%1$s Ticket #%2$s'),
+                $verb ?: $state,
+                $ticket->getNumber()
+                );
+
+        // Deleting?
+        if (!strcasecmp($state, 'deleted')) {
+
+            $info['placeholder'] = sprintf(__(
+                        'Optional reason for deleting %s'),
+                    __('this ticket'));
+            $info[ 'warn'] = sprintf(__(
+                        'Are you sure you want to DELETE %s?'),
+                        __('this ticket'));
+            //TODO: remove message below once we ship data retention plug
+            $info[ 'extra'] = sprintf('<strong>%s</strong>',
+                        __('Deleted tickets CANNOT be recovered, including any associated attachments.')
+                        );
+        }
+
+        $info['status_id'] = $info['status_id'] ?: $ticket->getStatusId();
+        $info['comments'] = Format::htmlchars($_REQUEST['comments']);
+
+        return self::_changeStatus($state, $info, $errors);
+    }
+
+    private function _changeStatus($state, $info=array(), $errors=array()) {
+
+        if ($info && isset($info['errors']))
+            $errors = array_merge($errors, $info['errors']);
+
+        if (!$info['error'] && isset($errors['err']))
+            $info['error'] = $errors['err'];
+
+        include(STAFFINC_DIR . 'templates/ticket-status.tmpl.php');
+    }
 }
 ?>
diff --git a/include/ajax.tips.php b/include/ajax.tips.php
index e81d1301b53622478520050bcd6e75528b28e36e..aa1c9bfe14355ba8b5bf9545483cc73b30cf29a0 100644
--- a/include/ajax.tips.php
+++ b/include/ajax.tips.php
@@ -23,10 +23,7 @@ class HelpTipAjaxAPI extends AjaxController {
     function getTipsJson($namespace, $lang=false) {
         global $ost, $thisstaff;
 
-        if (!$lang)
-            $lang = ($thisstaff)
-                ? $thisstaff->getLanguage()
-                : Internationalization::getDefaultLanguage();
+        $lang = Internationalization::getCurrentLanguage();
 
         $i18n = new Internationalization($lang);
         $tips = $i18n->getTemplate("help/tips/$namespace.yaml");
diff --git a/include/ajax.upgrader.php b/include/ajax.upgrader.php
index d021086ef1a55c84745b741dcebc70df160613d7..5877b84395d8f47bc831ea9dd68f9c7c2c9169c2 100644
--- a/include/ajax.upgrader.php
+++ b/include/ajax.upgrader.php
@@ -28,7 +28,7 @@ class UpgraderAjaxAPI extends AjaxController {
         $upgrader = new Upgrader(TABLE_PREFIX, UPGRADE_DIR.'streams/');
 
         if($upgrader->isAborted()) {
-            Http::response(416, "We have a problem ... wait a sec.");
+            Http::response(416, __("We have a problem ... wait a sec."));
             exit;
         }
 
@@ -41,22 +41,22 @@ class UpgraderAjaxAPI extends AjaxController {
                 $version = $upgrader->getNextVersion();
                 if($upgrader->upgrade()) {
                     //We're simply reporting progress here - call back will report next action'
-                    Http::response(200, "Upgraded to $version ... post-upgrade checks!");
+                    Http::response(200, sprintf(__("Upgraded to %s ... post-upgrade checks!"),$version));
                     exit;
                 }
             } else {
                 //Abort: Upgrade pending but NOT upgradable - invalid or wrong hash.
-                $upgrader->abort(sprintf('Upgrade Failed: Invalid or wrong hash [%s]',$ost->getDBSignature()));
+                $upgrader->abort(sprintf(__('Upgrade Failed: Invalid or wrong hash [%s]'),$ost->getDBSignature()));
             }
         } elseif(!$ost->isUpgradePending()) {
             $upgrader->setState('done');
             session_write_close();
-            Http::response(201, "We're done!");
+            Http::response(201, __("We're done!"));
             exit;
         }
 
         if($upgrader->isAborted() || $upgrader->getErrors()) {
-            Http::response(416, "We have a problem ... wait a sec.");
+            Http::response(416, __("We have a problem ... wait a sec."));
             exit;
         }
 
diff --git a/include/ajax.users.php b/include/ajax.users.php
index 103999b642b553a04d345e5b7a87c417187db15e..f211ff6e644238b29576f92313d8695e6f74cc51 100644
--- a/include/ajax.users.php
+++ b/include/ajax.users.php
@@ -26,7 +26,7 @@ class UsersAjaxAPI extends AjaxController {
     function search($type = null) {
 
         if(!isset($_REQUEST['q'])) {
-            Http::response(400, 'Query argument is required');
+            Http::response(400, __('Query argument is required'));
         }
 
         $limit = isset($_REQUEST['limit']) ? (int) $_REQUEST['limit']:25;
@@ -113,7 +113,7 @@ class UsersAjaxAPI extends AjaxController {
             Http::response(404, 'Unknown user');
 
         $info = array(
-            'title' => sprintf('Update %s', Format::htmlchars($user->getName()))
+            'title' => sprintf(__('Update %s'), Format::htmlchars($user->getName()))
         );
         $forms = $user->getForms();
 
@@ -129,7 +129,7 @@ class UsersAjaxAPI extends AjaxController {
             Http::response(404, 'Unknown user');
 
         $errors = array();
-        if($user->updateInfo($_POST, $errors))
+        if ($user->updateInfo($_POST, $errors, true) && !$errors)
              Http::response(201, $user->to_json());
 
         $forms = $user->getForms();
@@ -148,7 +148,7 @@ class UsersAjaxAPI extends AjaxController {
         if ($_POST) {
             // Register user on post
             if ($user->getAccount())
-                $info['error'] = 'User already registered';
+                $info['error'] = __('User already registered');
             elseif ($user->register($_POST, $errors))
                 Http::response(201, 'Account created successfully');
 
@@ -157,7 +157,7 @@ class UsersAjaxAPI extends AjaxController {
             if ($errors['err'])
                 $info['error'] = $errors['err'];
             else
-                $info['error'] = 'Unable to register user - try again!';
+                $info['error'] = __('Unable to register user - try again!');
         }
 
         include(STAFFINC_DIR . 'templates/user-register.tmpl.php');
@@ -187,7 +187,7 @@ class UsersAjaxAPI extends AjaxController {
             if ($errors['err'])
                 $info['error'] = $errors['err'];
             else
-                $info['error'] = 'Unable to update account - try again!';
+                $info['error'] = __('Unable to update account - try again!');
         }
 
         $info['_target'] = $target;
@@ -207,19 +207,19 @@ class UsersAjaxAPI extends AjaxController {
         if ($_POST) {
             if ($user->tickets->count()) {
                 if (!$thisstaff->canDeleteTickets()) {
-                    $info['error'] = 'You do not have permission to delete a user with tickets!';
+                    $info['error'] = __('You do not have permission to delete a user with tickets!');
                 } elseif ($_POST['deletetickets']) {
                     foreach($user->tickets as $ticket)
                         $ticket->delete();
                 } else {
-                    $info['error'] = 'You cannot delete a user with tickets!';
+                    $info['error'] = __('You cannot delete a user with tickets!');
                 }
             }
 
             if (!$info['error'] && $user->delete())
                  Http::response(204, 'User deleted successfully');
             elseif (!$info['error'])
-                $info['error'] = 'Unable to delete user - try again!';
+                $info['error'] = __('Unable to delete user - try again!');
         }
 
         include(STAFFINC_DIR . 'templates/user-delete.tmpl.php');
@@ -230,7 +230,7 @@ class UsersAjaxAPI extends AjaxController {
         if(($user=User::lookup(($id) ? $id : $_REQUEST['id'])))
            Http::response(201, $user->to_json());
 
-        $info = array('error' =>'Unknown or invalid user');
+        $info = array('error' => sprintf(__('%s: Unknown or invalid ID.'), _N('end user', 'end users', 1)));
 
         return self::_lookupform(null, $info);
     }
@@ -247,12 +247,12 @@ class UsersAjaxAPI extends AjaxController {
             $info['lookup'] = 'local';
 
         if ($_POST) {
-            $info['title'] = 'Add New User';
+            $info['title'] = __('Add New User');
             $form = UserForm::getUserForm()->getForm($_POST);
             if (($user = User::fromForm($form)))
                 Http::response(201, $user->to_json());
 
-            $info['error'] = 'Error adding user - try again!';
+            $info['error'] = __('Error adding user - try again!');
         }
 
         return self::_lookupform($form, $info);
@@ -270,9 +270,11 @@ class UsersAjaxAPI extends AjaxController {
             Http::response(404, 'User not found');
 
         $form = UserForm::getUserForm()->getForm($user_info);
-        $info = array('title' => 'Import Remote User');
+        $info = array('title' => __(
+            /* `remote` users are those in a remore directory such as LDAP */
+            'Import Remote User'));
         if (!$user_info)
-            $info['error'] = 'Unable to find user in directory';
+            $info['error'] = __('Unable to find user in directory');
 
         include(STAFFINC_DIR . 'templates/user-lookup.tmpl.php');
     }
@@ -284,7 +286,7 @@ class UsersAjaxAPI extends AjaxController {
             Http::response(403, 'Login Required');
 
         $info = array(
-            'title' => 'Import Users',
+            'title' => __('Import Users'),
             'action' => '#users/import',
             'upload_url' => "users.php?do=import-users",
         );
@@ -306,7 +308,7 @@ class UsersAjaxAPI extends AjaxController {
         if ($id)
             $user = User::lookup($id);
 
-        $info = array('title' => 'Select User');
+        $info = array('title' => __('Select User'));
 
         ob_start();
         include(STAFFINC_DIR . 'templates/user-lookup.tmpl.php');
@@ -319,7 +321,7 @@ class UsersAjaxAPI extends AjaxController {
     static function _lookupform($form=null, $info=array()) {
 
         if (!$info or !$info['title'])
-            $info += array('title' => 'Lookup or create a user');
+            $info += array('title' => __('Lookup or create a user'));
 
         ob_start();
         include(STAFFINC_DIR . 'templates/user-lookup.tmpl.php');
@@ -357,35 +359,38 @@ class UsersAjaxAPI extends AjaxController {
             Http::response(404, 'Unknown user');
 
         $info = array();
-        $info['title'] = 'Organization for '.Format::htmlchars($user->getName());
+        $info['title'] = sprintf(__('Organization for %s'),
+            Format::htmlchars($user->getName()));
         $info['action'] = '#users/'.$user->getId().'/org';
         $info['onselect'] = 'ajax.php/users/'.$user->getId().'/org';
 
         if ($_POST) {
             if ($_POST['orgid']) { //Existing org.
                 if (!($org = Organization::lookup($_POST['orgid'])))
-                    $info['error'] = 'Unknown organization selected';
+                    $info['error'] = __('Unknown organization selected');
             } else { //Creating new org.
                 $form = OrganizationForm::getDefaultForm()->getForm($_POST);
                 if (!($org = Organization::fromForm($form)))
-                    $info['error'] = 'Unable to create organization - try again!';
+                    $info['error'] = __('Unable to create organization.')
+                        .' '.__('Correct error(s) below and try again.');
             }
 
             if ($org && $user->setOrganization($org))
                 Http::response(201, $org->to_json());
             elseif (! $info['error'])
-                $info['error'] = 'Unable to add organization - try again!';
+                $info['error'] = __('Unable to add user to organization.')
+                    .' '.__('Correct error(s) below and try again.');
 
         } elseif ($orgId)
             $org = Organization::lookup($orgId);
         elseif ($org = $user->getOrganization()) {
-            $info['title'] = sprintf('%s &mdash; %s', Format::htmlchars($user->getName()), 'Organization');
+            $info['title'] = sprintf(__('%s &mdash; Organization'), Format::htmlchars($user->getName()));
             $info['action'] = $info['onselect'] = '';
             $tmpl = 'org.tmpl.php';
         }
 
         if ($org && $user->getOrgId() && $org->getId() != $user->getOrgId())
-            $info['warning'] = 'Are you sure you want to change user\'s organization?';
+            $info['warning'] = __("Are you sure you want to change user's organization?");
 
         $tmpl = $tmpl ?: 'org-lookup.tmpl.php';
 
diff --git a/include/api.cron.php b/include/api.cron.php
index 32d1b0aefb25b2ac546af5188191f5e896410b4f..633fd38c3875cddc8d4054d5b85b2e2f1b51f69b 100644
--- a/include/api.cron.php
+++ b/include/api.cron.php
@@ -7,7 +7,7 @@ class CronApiController extends ApiController {
     function execute() {
 
         if(!($key=$this->requireApiKey()) || !$key->canExecuteCron())
-            return $this->exerr(401, 'API key not authorized');
+            return $this->exerr(401, __('API key not authorized'));
 
         $this->run();
     }
@@ -18,7 +18,7 @@ class CronApiController extends ApiController {
 
         Cron::run();
        
-        $ost->logDebug('Cron Job','Cron job executed ['.$_SERVER['REMOTE_ADDR'].']');
+        $ost->logDebug(__('Cron Job'),__('Cron job executed').' ['.$_SERVER['REMOTE_ADDR'].']');
         $this->response(200,'Completed');
     }
 }
diff --git a/include/api.tickets.php b/include/api.tickets.php
index fcb55d81b5b27cc62ee8b3cfb71d4128f0344fab..6371daa632ff5240b0bee90cb3bed69c0ac56384 100644
--- a/include/api.tickets.php
+++ b/include/api.tickets.php
@@ -58,32 +58,35 @@ class TicketApiController extends ApiController {
 
         //Call parent to Validate the structure
         if(!parent::validate($data, $format, $strict) && $strict)
-            $this->exerr(400, 'Unexpected or invalid data received');
+            $this->exerr(400, __('Unexpected or invalid data received'));
 
-        //Nuke attachments IF API files are not allowed.
-        if(!$ost->getConfig()->allowAPIAttachments())
+        // Use the settings on the thread entry on the ticket details
+        // form to validate the attachments in the email
+        $tform = TicketForm::objects()->one()->getForm();
+        $messageField = $tform->getField('message');
+        $fileField = $messageField->getWidget()->getAttachments();
+
+        // Nuke attachments IF API files are not allowed.
+        if (!$messageField->isAttachmentsEnabled())
             $data['attachments'] = array();
 
         //Validate attachments: Do error checking... soft fail - set the error and pass on the request.
-        if($data['attachments'] && is_array($data['attachments'])) {
-            foreach($data['attachments'] as &$attachment) {
-                if(!$ost->isFileTypeAllowed($attachment))
-                    $attachment['error'] = 'Invalid file type (ext) for '.Format::htmlchars($attachment['name']);
-                elseif ($attachment['encoding'] && !strcasecmp($attachment['encoding'], 'base64')) {
-                    if(!($attachment['data'] = base64_decode($attachment['data'], true)))
-                        $attachment['error'] = sprintf('%s: Poorly encoded base64 data', Format::htmlchars($attachment['name']));
+        if ($data['attachments'] && is_array($data['attachments'])) {
+            foreach($data['attachments'] as &$file) {
+                if ($file['encoding'] && !strcasecmp($file['encoding'], 'base64')) {
+                    if(!($file['data'] = base64_decode($file['data'], true)))
+                        $file['error'] = sprintf(__('%s: Poorly encoded base64 data'),
+                            Format::htmlchars($file['name']));
+                }
+                // Validate and save immediately
+                try {
+                    $file['id'] = $fileField->uploadAttachment($file);
                 }
-                if (!$attachment['error']
-                        && ($size = $ost->getConfig()->getMaxFileSize())
-                        && ($fsize = $attachment['size'] ?: strlen($attachment['data']))
-                        && $fsize > $size) {
-                    $attachment['error'] = sprintf('File %s (%s) is too big. Maximum of %s allowed',
-                            Format::htmlchars($attachment['name']),
-                            Format::file_size($fsize),
-                            Format::file_size($size));
+                catch (FileUploadError $ex) {
+                    $file['error'] = $file['name'] . ': ' . $ex->getMessage();
                 }
             }
-            unset($attachment);
+            unset($file);
         }
 
         return true;
@@ -93,7 +96,7 @@ class TicketApiController extends ApiController {
     function create($format) {
 
         if(!($key=$this->requireApiKey()) || !$key->canCreateTickets())
-            return $this->exerr(401, 'API key not authorized');
+            return $this->exerr(401, __('API key not authorized'));
 
         $ticket = null;
         if(!strcasecmp($format, 'email')) {
@@ -105,7 +108,7 @@ class TicketApiController extends ApiController {
         }
 
         if(!$ticket)
-            return $this->exerr(500, "Unable to create new ticket: unknown error");
+            return $this->exerr(500, __("Unable to create new ticket: unknown error"));
 
         $this->response(201, $ticket->getNumber());
     }
@@ -126,15 +129,15 @@ class TicketApiController extends ApiController {
         # Return errors (?)
         if (count($errors)) {
             if(isset($errors['errno']) && $errors['errno'] == 403)
-                return $this->exerr(403, 'Ticket denied');
+                return $this->exerr(403, __('Ticket denied'));
             else
                 return $this->exerr(
                         400,
-                        "Unable to create new ticket: validation errors:\n"
+                        __("Unable to create new ticket: validation errors").":\n"
                         .Format::array_implode(": ", "\n", $errors)
                         );
         } elseif (!$ticket) {
-            return $this->exerr(500, "Unable to create new ticket: unknown error");
+            return $this->exerr(500, __("Unable to create new ticket: unknown error"));
         }
 
         return $ticket;
@@ -146,6 +149,10 @@ class TicketApiController extends ApiController {
             $data = $this->getEmailRequest();
 
         if (($thread = ThreadEntry::lookupByEmailHeaders($data))
+                && ($t=$thread->getTicket())
+                && ($data['staffId']
+                    || !$t->isClosed()
+                    || $t->isReopenable())
                 && $thread->postEmail($data)) {
             return $thread->getTicket();
         }
@@ -196,7 +203,7 @@ class PipeApiController extends TicketApiController {
         if(($ticket=$pipe->processEmail()))
            return $pipe->response(201, $ticket->getNumber());
 
-        return $pipe->exerr(416, 'Request failed - retry again!');
+        return $pipe->exerr(416, __('Request failed - retry again!'));
     }
 }
 
diff --git a/include/class.ajax.php b/include/class.ajax.php
index 3be9713d89be8d1d2391f4c49c22919a5baccf78..870d5ae88aaf3fd1cea9392a36c566704ceea67a 100644
--- a/include/class.ajax.php
+++ b/include/class.ajax.php
@@ -31,7 +31,7 @@ class AjaxController extends ApiController {
     function staffOnly() {
         global $thisstaff;
         if(!$thisstaff || !$thisstaff->isValid()) {
-            Http::response(401,'Access Denied. IP '.$_SERVER['REMOTE_ADDR']);
+            Http::response(401,sprintf(__('Access Denied. IP %s'),$_SERVER['REMOTE_ADDR']));
         }
     }
     /**
diff --git a/include/class.api.php b/include/class.api.php
index 5fe585eb2e82730779ca4bf2e9a5b883ce91ed71..0fd2c17e3f0ab552513c775e5f2f93ee52bc1e7f 100644
--- a/include/class.api.php
+++ b/include/class.api.php
@@ -122,7 +122,7 @@ class API {
     function save($id, $vars, &$errors) {
 
         if(!$id && (!$vars['ipaddr'] || !Validator::is_ip($vars['ipaddr'])))
-            $errors['ipaddr'] = 'Valid IP required';
+            $errors['ipaddr'] = __('Valid IP is required');
 
         if($errors) return false;
 
@@ -137,7 +137,8 @@ class API {
             if(db_query($sql))
                 return true;
 
-            $errors['err']='Unable to update API key. Internal error occurred';
+            $errors['err']=sprintf(__('Unable to update %s.'), __('this API key'))
+               .' '.__('Internal error occurred');
 
         } else {
             $sql='INSERT INTO '.API_KEY_TABLE.' SET '.$sql
@@ -148,7 +149,8 @@ class API {
             if(db_query($sql) && ($id=db_insert_id()))
                 return $id;
 
-            $errors['err']='Unable to add API key. Try again!';
+            $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                __('this API key'));
         }
 
         return false;
@@ -171,9 +173,9 @@ class ApiController {
         # header
 
         if(!($key=$this->getApiKey()))
-            return $this->exerr(401, 'Valid API key required');
+            return $this->exerr(401, __('Valid API key required'));
         elseif (!$key->isActive() || $key->getIPAddr()!=$_SERVER['REMOTE_ADDR'])
-            return $this->exerr(401, 'API key not found/active or source IP not authorized');
+            return $this->exerr(401, __('API key not found/active or source IP not authorized'));
 
         return $key;
     }
@@ -197,13 +199,13 @@ class ApiController {
         $input = $ost->is_cli()?'php://stdin':'php://input';
 
         if (!($stream = @fopen($input, 'r')))
-            $this->exerr(400, "Unable to read request body");
+            $this->exerr(400, __("Unable to read request body"));
 
         $parser = null;
         switch(strtolower($format)) {
             case 'xml':
                 if (!function_exists('xml_parser_create'))
-                    $this->exerr(501, 'XML extension not supported');
+                    $this->exerr(501, __('XML extension not supported'));
 
                 $parser = new ApiXmlDataParser();
                 break;
@@ -214,7 +216,7 @@ class ApiController {
                 $parser = new ApiEmailDataParser();
                 break;
             default:
-                $this->exerr(415, 'Unsupported data format');
+                $this->exerr(415, __('Unsupported data format'));
         }
 
         if (!($data = $parser->parse($stream)))
@@ -255,10 +257,10 @@ class ApiController {
                 continue;
             }
             if ($strict)
-                return $this->exerr(400, "$prefix$key: Unexpected data received");
+                return $this->exerr(400, sprintf(__("%s: Unexpected data received in API request"), "$prefix$key"));
             else
-                $ost->logWarning('API Unexpected Data',
-                    "$prefix$key: Unexpected data received in API request",
+                $ost->logWarning(__('API Unexpected Data'),
+                    sprintf(__("%s: Unexpected data received in API request"), "$prefix$key"),
                     false);
         }
 
@@ -293,7 +295,7 @@ class ApiController {
         $msg = $error;
         if($_SERVER['HTTP_X_API_KEY'])
             $msg.="\n*[".$_SERVER['HTTP_X_API_KEY']."]*\n";
-        $ost->logWarning("API Error ($code)", $msg, false);
+        $ost->logWarning(__('API Error')." ($code)", $msg, false);
 
         $this->response($code, $error); //Responder should exit...
         return false;
@@ -319,8 +321,8 @@ class ApiXmlDataParser extends XmlDataParser {
     function fixup($current) {
         global $cfg;
 
-        if($current['ticket'])
-            $current = $current['ticket'];
+		if($current['ticket'])
+			$current = $current['ticket'];
 
         if (!is_array($current))
             return $current;
diff --git a/include/class.attachment.php b/include/class.attachment.php
index 937d09edd5346d331170752077e1b00f9a354e51..d562b31a095fa69251f62af687d14f40dfda0243 100644
--- a/include/class.attachment.php
+++ b/include/class.attachment.php
@@ -120,26 +120,37 @@ class GenericAttachments {
         $i=array();
         if (!is_array($files)) $files=array($files);
         foreach ($files as $file) {
-            if (($fileId = is_numeric($file)
-                    ? $file : AttachmentFile::upload($file))
-                    && is_numeric($fileId)) {
-                $sql ='INSERT INTO '.ATTACHMENT_TABLE
-                    .' SET `type`='.db_input($this->getType())
-                    .',object_id='.db_input($this->getId())
-                    .',file_id='.db_input($fileId)
-                    .',inline='.db_input($inline ? 1 : 0);
-                // File may already be associated with the draft (in the
-                // event it was deleted and re-added)
-                if (db_query($sql, function($errno) { return $errno != 1062; })
-                        || db_errno() == 1062)
-                    $i[] = $fileId;
-            }
+            if (is_numeric($file))
+                $fileId = $file;
+            elseif (is_array($file) && isset($file['id']))
+                $fileId = $file['id'];
+            elseif (!($fileId = AttachmentFile::upload($file)))
+                continue;
+
+            $_inline = isset($file['inline']) ? $file['inline'] : $inline;
+
+            $sql ='INSERT INTO '.ATTACHMENT_TABLE
+                .' SET `type`='.db_input($this->getType())
+                .',object_id='.db_input($this->getId())
+                .',file_id='.db_input($fileId)
+                .',inline='.db_input($_inline ? 1 : 0);
+            // File may already be associated with the draft (in the
+            // event it was deleted and re-added)
+            if (db_query($sql, function($errno) { return $errno != 1062; })
+                    || db_errno() == 1062)
+                $i[] = $fileId;
         }
+
         return $i;
     }
 
-    function save($info, $inline=true) {
-        if (!($fileId = AttachmentFile::save($info)))
+    function save($file, $inline=true) {
+
+        if (is_numeric($file))
+            $fileId = $file;
+        elseif (is_array($file) && isset($file['id']))
+            $fileId = $file['id'];
+        elseif (!($fileId = AttachmentFile::save($file)))
             return false;
 
         $sql ='INSERT INTO '.ATTACHMENT_TABLE
@@ -167,6 +178,8 @@ class GenericAttachments {
                 .' AND a.object_id='.db_input($this->getId());
             if(($res=db_query($sql)) && db_num_rows($res)) {
                 while($rec=db_fetch_array($res)) {
+                    $rec['download'] = AttachmentFile::getDownloadForIdAndKey(
+                        $rec['id'], $rec['key']);
                     $this->attachments[] = $rec;
                 }
             }
diff --git a/include/class.auth.php b/include/class.auth.php
index fb5bcb8d94ff00c4bdeab101414515e76c18c2cc..4a97ab01adb25d60a6bfb74f69628de824772c4a 100644
--- a/include/class.auth.php
+++ b/include/class.auth.php
@@ -221,7 +221,7 @@ abstract class AuthenticationBackend {
         }
 
         if (!$result)
-            $result = new AccessDenied('Access denied');
+            $result = new AccessDenied(__('Access denied'));
 
         if ($result && $result instanceof AccessDenied)
             $errors['err'] = $result->reason;
@@ -266,7 +266,7 @@ abstract class AuthenticationBackend {
         }
 
         if (!$result && $forcedAuth)
-            $result = new  AccessDenied('Unknown user');
+            $result = new  AccessDenied(__('Unknown user'));
 
         if ($result && $result instanceof AccessDenied)
             $errors['err'] = $result->reason;
@@ -423,8 +423,8 @@ abstract class StaffAuthenticationBackend  extends AuthenticationBackend {
             return false;
 
         //Log debug info.
-        $ost->logDebug('Staff login',
-            sprintf("%s logged in [%s], via %s", $staff->getUserName(),
+        $ost->logDebug(_S('Agent login'),
+            sprintf(_S("%s logged in [%s], via %s"), $staff->getUserName(),
                 $_SERVER['REMOTE_ADDR'], get_class($bk))); //Debug.
 
         $sql='UPDATE '.STAFF_TABLE.' SET lastlogin=NOW() '
@@ -466,8 +466,8 @@ abstract class StaffAuthenticationBackend  extends AuthenticationBackend {
 
         $_SESSION['_auth']['staff'] = array();
         unset($_SESSION[':token']['staff']);
-        $ost->logDebug('Staff logout',
-                sprintf("%s logged out [%s]",
+        $ost->logDebug(_S('Agent logout'),
+                sprintf(_S("%s logged out [%s]"),
                     $staff->getUserName(),
                     $_SERVER['REMOTE_ADDR'])); //Debug.
 
@@ -600,9 +600,9 @@ abstract class UserAuthenticationBackend  extends AuthenticationBackend {
 
         if ($acct) {
             if (!$acct->isConfirmed())
-                throw new AccessDenied('Account confirmation required');
+                throw new AccessDenied(__('Account confirmation required'));
             elseif ($acct->isLocked())
-                throw new AccessDenied('Account is administratively locked');
+                throw new AccessDenied(__('Account is administratively locked'));
         }
 
         // Tag the user and associated ticket in the SESSION
@@ -620,9 +620,10 @@ abstract class UserAuthenticationBackend  extends AuthenticationBackend {
         }
 
         //Log login info...
-        $msg=sprintf('%s (%s) logged in [%s]',
+        $msg=sprintf(_S('%1$s (%2$s) logged in [%3$s]'
+                /* Tokens are <username>, <id>, and <ip> */),
                 $user->getUserName(), $user->getId(), $_SERVER['REMOTE_ADDR']);
-        $ost->logDebug('User login', $msg);
+        $ost->logDebug(_S('User login'), $msg);
 
         if ($bk->supportsInteractiveAuthentication() && ($acct=$user->getAccount()))
             $acct->cancelResetTokens();
@@ -653,9 +654,9 @@ abstract class UserAuthenticationBackend  extends AuthenticationBackend {
 
         $_SESSION['_auth']['user'] = array();
         unset($_SESSION[':token']['client']);
-        $ost->logDebug('User logout',
-                sprintf("%s logged out [%s]",
-                    $user->getUserName(), $_SERVER['REMOTE_ADDR']));
+        $ost->logDebug(_S('User logout'),
+            sprintf(_S("%s logged out [%s]" /* Tokens are <username> and <ip> */),
+                $user->getUserName(), $_SERVER['REMOTE_ADDR']));
     }
 
     protected function getAuthKey($user) {
@@ -701,18 +702,18 @@ abstract class ExternalUserAuthenticationBackend
     static $service_name = "External";
 
     function renderExternalLink() { ?>
-        <a class="external-sign-in" title="Sign in with <?php echo static::$service_name; ?>"
+        <a class="external-sign-in" title="<?php echo sprintf(__('Sign in with %s'), __(static::$service_name)); ?>"
                 href="login.php?do=ext&amp;bk=<?php echo urlencode(static::$id); ?>">
 <?php if (static::$sign_in_image_url) { ?>
         <img class="sign-in-image" src="<?php echo static::$sign_in_image_url;
-            ?>" alt="Sign in with <?php echo static::$service_name; ?>"/>
+            ?>" alt="<?php echo sprintf(__('Sign in with %s'), __(static::$service_name)); ?>"/>
 <?php } else { ?>
             <div class="external-auth-box">
             <span class="external-auth-icon">
                 <i class="icon-<?php echo static::$fa_icon; ?> icon-large icon-fixed-with"></i>
             </span>
             <span class="external-auth-name">
-                Sign in with <?php echo static::$service_name; ?>
+                <?php echo sprintf(__('Sign in with %s'), __(static::$service_name)); ?>
             </span>
             </div>
 <?php } ?>
@@ -803,7 +804,7 @@ class StaffAuthStrikeBackend extends  AuthStrikeBackend {
         //Veto login due to excessive login attempts.
         if((time()-$authsession['laststrike'])<$cfg->getStaffLoginTimeout()) {
             $authsession['laststrike'] = time(); //reset timer.
-            return new AccessDenied('Max. failed login attempts reached');
+            return new AccessDenied(__('Maximum failed login attempts reached'));
         }
 
         //Timeout is over.
@@ -824,22 +825,24 @@ class StaffAuthStrikeBackend extends  AuthStrikeBackend {
         $authsession['strikes']+=1;
         if($authsession['strikes']>$cfg->getStaffMaxLogins()) {
             $authsession['laststrike']=time();
-            $alert='Excessive login attempts by a staff member?'."\n".
-                   'Username: '.$username."\n"
-                   .'IP: '.$_SERVER['REMOTE_ADDR']."\n"
-                   .'TIME: '.date('M j, Y, g:i a T')."\n\n"
-                   .'Attempts #'.$authsession['strikes']."\n"
-                   .'Timeout: '.($cfg->getStaffLoginTimeout()/60)." minutes \n\n";
-            $ost->logWarning('Excessive login attempts ('.$username.')', $alert,
-                    $cfg->alertONLoginError());
-            return new AccessDenied('Forgot your login info? Contact Admin.');
+            $timeout = $cfg->getStaffLoginTimeout()/60;
+            $alert=_S('Excessive login attempts by an agent?')."\n"
+                   ._S('Username').": $username\n"
+                   ._S('IP').": {$_SERVER['REMOTE_ADDR']}\n"
+                   ._S('Time').": ".date('M j, Y, g:i a T')."\n\n"
+                   ._S('Attempts').": {$authsession['strikes']}\n"
+                   ._S('Timeout').": ".sprintf(_N('%d minute', '%d minutes', $timeout), $timeout)."\n\n";
+            $ost->logWarning(sprintf(_S('Excessive login attempts (%s)'),$username),
+                    $alert, $cfg->alertONLoginError());
+            return new AccessDenied(__('Forgot your login info? Contact Admin.'));
         //Log every other third failed login attempt as a warning.
         } elseif($authsession['strikes']%3==0) {
-            $alert='Username: '.$username."\n"
-                    .'IP: '.$_SERVER['REMOTE_ADDR']."\n"
-                    .'TIME: '.date('M j, Y, g:i a T')."\n\n"
-                    .'Attempts #'.$authsession['strikes'];
-            $ost->logWarning('Failed staff login attempt ('.$username.')', $alert, false);
+            $alert=_S('Username').": {$username}\n"
+                    ._S('IP').": {$_SERVER['REMOTE_ADDR']}\n"
+                    ._S('Time').": ".date('M j, Y, g:i a T')."\n\n"
+                    ._S('Attempts').": {$authsession['strikes']}";
+            $ost->logWarning(sprintf(_S('Failed agent login attempt (%s)'),$username),
+                $alert, false);
         }
     }
 }
@@ -862,7 +865,7 @@ class UserAuthStrikeBackend extends  AuthStrikeBackend {
         //Veto login due to excessive login attempts.
         if ((time()-$authsession['laststrike']) < $cfg->getStaffLoginTimeout()) {
             $authsession['laststrike'] = time(); //reset timer.
-            return new AccessDenied("You've reached maximum failed login attempts allowed.");
+            return new AccessDenied(__("You've reached maximum failed login attempts allowed."));
         }
 
         //Timeout is over.
@@ -884,16 +887,19 @@ class UserAuthStrikeBackend extends  AuthStrikeBackend {
         $authsession['strikes']+=1;
         if($authsession['strikes']>$cfg->getClientMaxLogins()) {
             $authsession['laststrike'] = time();
-            $alert='Excessive login attempts by a user.'."\n".
-                    'Username: '.$username."\n".
-                    'IP: '.$_SERVER['REMOTE_ADDR']."\n".'Time:'.date('M j, Y, g:i a T')."\n\n".
-                    'Attempts #'.$authsession['strikes'];
-            $ost->logError('Excessive login attempts (user)', $alert, ($cfg->alertONLoginError()));
-            return new AccessDenied('Access Denied');
+            $alert=_S('Excessive login attempts by a user.')."\n".
+                    _S('Username').": {$username}\n".
+                    _S('IP').": {$_SERVER['REMOTE_ADDR']}\n".
+                    _S('Time').": ".date('M j, Y, g:i a T')."\n\n".
+                    _S('Attempts').": {$authsession['strikes']}";
+            $ost->logError(_S('Excessive login attempts (user)'), $alert, ($cfg->alertONLoginError()));
+            return new AccessDenied(__('Access Denied'));
         } elseif($authsession['strikes']%3==0) { //Log every third failed login attempt as a warning.
-            $alert='Username: '.$username."\n".'IP: '.$_SERVER['REMOTE_ADDR'].
-                   "\n".'TIME: '.date('M j, Y, g:i a T')."\n\n".'Attempts #'.$authsession['strikes'];
-            $ost->logWarning('Failed login attempt (user)', $alert);
+            $alert=_S('Username').": {$username}\n".
+                    _S('IP').": {$_SERVER['REMOTE_ADDR']}\n".
+                    _S('Time').": ".date('M j, Y, g:i a T')."\n\n".
+                    _S('Attempts').": {$authsession['strikes']}";
+            $ost->logWarning(_S('Failed login attempt (user)'), $alert);
         }
 
     }
@@ -939,15 +945,15 @@ class PasswordResetTokenBackend extends StaffAuthenticationBackend {
             return false;
         elseif (($staff = new StaffSession($_POST['userid'])) &&
                 !$staff->getId())
-            $errors['msg'] = 'Invalid user-id given';
+            $errors['msg'] = __('Invalid user-id given');
         elseif (!($id = $_config->get($_POST['token']))
                 || $id != $staff->getId())
-            $errors['msg'] = 'Invalid reset token';
+            $errors['msg'] = __('Invalid reset token');
         elseif (!($ts = $_config->lastModified($_POST['token']))
                 && ($ost->getConfig()->getPwResetWindow() < (time() - strtotime($ts))))
-            $errors['msg'] = 'Invalid reset token';
+            $errors['msg'] = __('Invalid reset token');
         elseif (!$staff->forcePasswdRest())
-            $errors['msg'] = 'Unable to reset password';
+            $errors['msg'] = __('Unable to reset password');
         else
             return $staff;
     }
@@ -1152,15 +1158,15 @@ class ClientPasswordResetTokenBackend extends UserAuthenticationBackend {
         elseif (!($acct = ClientAccount::lookupByUsername($_POST['userid']))
                 || !$acct->getId()
                 || !($client = new ClientSession(new EndUser($acct->getUser()))))
-            $errors['msg'] = 'Invalid user-id given';
+            $errors['msg'] = __('Invalid user-id given');
         elseif (!($id = $_config->get($_POST['token']))
                 || $id != $client->getId())
-            $errors['msg'] = 'Invalid reset token';
+            $errors['msg'] = __('Invalid reset token');
         elseif (!($ts = $_config->lastModified($_POST['token']))
                 && ($ost->getConfig()->getPwResetWindow() < (time() - strtotime($ts))))
-            $errors['msg'] = 'Invalid reset token';
+            $errors['msg'] = __('Invalid reset token');
         elseif (!$acct->forcePasswdReset())
-            $errors['msg'] = 'Unable to reset password';
+            $errors['msg'] = __('Unable to reset password');
         else
             return $client;
     }
diff --git a/include/class.banlist.php b/include/class.banlist.php
index 590bacb7a4d64e4f2b8907a1731263614897a9b9..939ae179b1cc142950daf65cd4c25f301c99b651 100644
--- a/include/class.banlist.php
+++ b/include/class.banlist.php
@@ -15,7 +15,6 @@
 **********************************************************************/
 
 require_once "class.filter.php";
-
 class Banlist {
     
     function add($email,$submitter='') {
@@ -52,7 +51,7 @@ class Banlist {
             'match_all_rules' => false,
             'reject_ticket'  => true,
             'rules'         => array(),
-            'notes'         => 'Internal list for email banning. Do not remove'
+            'notes'         => __('Internal list for email banning. Do not remove')
         ), $errors);
     }
 
diff --git a/include/class.canned.php b/include/class.canned.php
index bfb5e82ce5e09a6eca8dab6f16aeda2e0485cc4f..a7154b974ef56d877c1bead681c8cb9fd4e081f2 100644
--- a/include/class.canned.php
+++ b/include/class.canned.php
@@ -242,17 +242,17 @@ class Canned {
         $vars['title']=Format::striptags(trim($vars['title']));
 
         if($id && $id!=$vars['id'])
-            $errors['err']='Internal error. Try again';
+            $errors['err']=__('Internal error. Try again');
 
         if(!$vars['title'])
-            $errors['title']='Title required';
+            $errors['title']=__('Title required');
         elseif(strlen($vars['title'])<3)
-            $errors['title']='Title is too short. 3 chars minimum';
+            $errors['title']=__('Title is too short. 3 chars minimum');
         elseif(($cid=self::getIdByTitle($vars['title'])) && $cid!=$id)
-            $errors['title']='Title already exists';
+            $errors['title']=__('Title already exists');
 
         if(!$vars['response'])
-            $errors['response']='Response text required';
+            $errors['response']=__('Response text is required');
 
         if($errors) return false;
 
@@ -268,14 +268,15 @@ class Canned {
             if(db_query($sql))
                 return true;
 
-            $errors['err']='Unable to update canned response.';
+            $errors['err']=sprintf(__('Unable to update %s.'), __('this canned response'));
 
         } else {
             $sql='INSERT INTO '.CANNED_TABLE.' SET '.$sql.',created=NOW()';
             if(db_query($sql) && ($id=db_insert_id()))
                 return $id;
 
-            $errors['err']='Unable to create the canned response. Internal error';
+            $errors['err']=sprintf(__('Unable to create %s.'), __('this canned response'))
+               .' '.__('Internal error occurred');
         }
 
         return false;
diff --git a/include/class.category.php b/include/class.category.php
index ba705b41296cc816fbc290a4e08072f071ccec3f..5f67c6ee5d182e1b837a4fdc8187f05d0bb40c66 100644
--- a/include/class.category.php
+++ b/include/class.category.php
@@ -119,20 +119,20 @@ class Category {
 
         //Cleanup.
         $vars['name']=Format::striptags(trim($vars['name']));
-      
+
         //validate
         if($id && $id!=$vars['id'])
-            $errors['err']='Internal error. Try again';
-      
+            $errors['err']=__('Internal error occurred');
+
         if(!$vars['name'])
-            $errors['name']='Category name is required';
+            $errors['name']=__('Category name is required');
         elseif(strlen($vars['name'])<3)
-            $errors['name']='Name is too short. 3 chars minimum';
+            $errors['name']=__('Name is too short. 3 chars minimum');
         elseif(($cid=self::findIdByName($vars['name'])) && $cid!=$id)
-            $errors['name']='Category already exists';
+            $errors['name']=__('Category already exists');
 
         if(!$vars['description'])
-            $errors['description']='Category description is required';
+            $errors['description']=__('Category description is required');
 
         if($errors) return false;
 
@@ -151,14 +151,15 @@ class Category {
             if(db_query($sql))
                 return true;
 
-            $errors['err']='Unable to update FAQ category.';
+            $errors['err']=sprintf(__('Unable to update %s.'), __('this FAQ category'));
 
         } else {
             $sql='INSERT INTO '.FAQ_CATEGORY_TABLE.' SET '.$sql.',created=NOW()';
             if(db_query($sql) && ($id=db_insert_id()))
                 return $id;
 
-            $errors['err']='Unable to create FAQ category. Internal error';
+            $errors['err']=sprintf(__('Unable to create %s.'), __('this FAQ category'))
+               .' '.__('Internal error occurred');
         }
 
         return false;
diff --git a/include/class.client.php b/include/class.client.php
index 6705b65c67ea1732e8f35cc3f5ac1f10dc0478f8..bfcffeaca09f2a1251a0d3ae7fd8125cd5d08e1b 100644
--- a/include/class.client.php
+++ b/include/class.client.php
@@ -247,7 +247,10 @@ class  EndUser extends AuthenticatedUser {
     }
 
     function getNumTickets() {
-        return ($stats=$this->getTicketStats())?($stats['open']+$stats['closed']):0;
+        if (!($stats=$this->getTicketStats()))
+            return 0;
+
+        return $stats['open']+$stats['closed'];
     }
 
     function getNumOpenTickets() {
@@ -266,21 +269,51 @@ 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 '
-            .' FROM '.TICKET_TABLE.' ticket '
-            .' LEFT JOIN '.TICKET_TABLE.' open
-                ON (open.ticket_id=ticket.ticket_id AND open.status=\'open\') '
-            .' LEFT JOIN '.TICKET_TABLE.' closed
-                ON (closed.ticket_id=ticket.ticket_id AND closed.status=\'closed\')'
-            .' LEFT JOIN '.TICKET_COLLABORATOR_TABLE.' collab
-                ON (collab.ticket_id=ticket.ticket_id
-                    AND collab.user_id = '.db_input($this->getId()).' )'
-            .' WHERE ticket.user_id = '.db_input($this->getId())
-            .' OR collab.user_id = '.db_input($this->getId());
-
-        return db_fetch_array(db_query($sql));
+        $where = ' WHERE ticket.user_id = '.db_input($this->getId())
+                .' OR collab.user_id = '.db_input($this->getId()).' ';
+
+        $join  =  'LEFT JOIN '.TICKET_COLLABORATOR_TABLE.' collab
+                    ON (collab.ticket_id=ticket.ticket_id
+                            AND collab.user_id = '.db_input($this->getId()).' ) ';
+
+        $sql =  'SELECT \'open\', count( ticket.ticket_id ) AS tickets '
+                .'FROM ' . TICKET_TABLE . ' ticket '
+                .'INNER JOIN '.TICKET_STATUS_TABLE. ' status
+                    ON (ticket.status_id=status.id
+                            AND status.state=\'open\') '
+                . $join
+                . $where
+
+                .'UNION SELECT \'closed\', count( ticket.ticket_id ) AS tickets '
+                .'FROM ' . TICKET_TABLE . ' ticket '
+                .'INNER JOIN '.TICKET_STATUS_TABLE. ' status
+                    ON (ticket.status_id=status.id
+                            AND status.state=\'closed\' ) '
+                . $join
+                . $where;
+
+        $res = db_query($sql);
+        $stats = array();
+        while($row = db_fetch_row($res)) {
+            $stats[$row[0]] = $row[1];
+        }
+
+        return $stats;
     }
 }
 
@@ -328,39 +361,43 @@ class ClientAccount extends UserAccount {
         if ($vars['passwd1'] || $vars['passwd2'] || $vars['cpasswd'] || $rtoken) {
 
             if (!$vars['passwd1'])
-                $errors['passwd1']='New password required';
+                $errors['passwd1']=__('New password is required');
             elseif ($vars['passwd1'] && strlen($vars['passwd1'])<6)
-                $errors['passwd1']='Must be at least 6 characters';
+                $errors['passwd1']=__('Password must be at least 6 characters');
             elseif ($vars['passwd1'] && strcmp($vars['passwd1'], $vars['passwd2']))
-                $errors['passwd2']='Password(s) do not match';
+                $errors['passwd2']=__('Passwords do not match');
 
             if ($rtoken) {
                 $_config = new Config('pwreset');
                 if ($_config->get($rtoken) != $this->getUserId())
                     $errors['err'] =
-                        'Invalid reset token. Logout and try again';
+                        __('Invalid reset token. Logout and try again');
                 elseif (!($ts = $_config->lastModified($rtoken))
                         && ($cfg->getPwResetWindow() < (time() - strtotime($ts))))
                     $errors['err'] =
-                        'Invalid reset token. Logout and try again';
+                        __('Invalid reset token. Logout and try again');
             }
             elseif ($this->get('passwd')) {
                 if (!$vars['cpasswd'])
-                    $errors['cpasswd']='Current password required';
+                    $errors['cpasswd']=__('Current password is required');
                 elseif (!$this->hasCurrentPassword($vars['cpasswd']))
-                    $errors['cpasswd']='Invalid current password!';
+                    $errors['cpasswd']=__('Invalid current password!');
                 elseif (!strcasecmp($vars['passwd1'], $vars['cpasswd']))
-                    $errors['passwd1']='New password MUST be different from the current password!';
+                    $errors['passwd1']=__('New password MUST be different from the current password!');
             }
         }
 
         if (!$vars['timezone_id'])
-            $errors['timezone_id']='Time zone required';
+            $errors['timezone_id']=__('Time zone selection is required');
 
         if ($errors) return false;
 
         $this->set('timezone_id', $vars['timezone_id']);
         $this->set('dst', isset($vars['dst']) ? 1 : 0);
+        // Change language
+        $this->set('lang', $vars['lang'] ?: null);
+        $_SESSION['client:lang'] = null;
+        TextDomain::configureForUser($this);
 
         if ($vars['backend']) {
             $this->set('backend', $vars['backend']);
diff --git a/include/class.collaborator.php b/include/class.collaborator.php
index b8adb93262d1dba483457b7decd82b8af7dd6078..b123644a0d85b3276f2a8e3c9403adaaaa9b60f4 100644
--- a/include/class.collaborator.php
+++ b/include/class.collaborator.php
@@ -96,9 +96,9 @@ class Collaborator extends TicketUser {
     static function add($info, &$errors) {
 
         if (!$info || !$info['ticketId'] || !$info['userId'])
-            $errors['err'] = 'Invalid or missing information';
+            $errors['err'] = __('Invalid or missing information');
         elseif (($c=self::lookup($info)))
-            $errors['err'] = sprintf('%s is already a collaborator',
+            $errors['err'] = sprintf(__('%s is already a collaborator'),
                     $c->getName());
 
         if ($errors) return false;
@@ -112,7 +112,7 @@ class Collaborator extends TicketUser {
         if(db_query($sql) && ($id=db_insert_id()))
             return self::lookup($id);
 
-        $errors['err'] = 'Unable to add collaborator. Internal error';
+        $errors['err'] = __('Unable to add collaborator. Internal error');
 
         return false;
     }
diff --git a/include/class.config.php b/include/class.config.php
index 2d5c65e81c9cde2fa468afe69d237c25c2967e15..8a7d5eebdb6d53a6d23d5667d543ded34ad8b5ca 100644
--- a/include/class.config.php
+++ b/include/class.config.php
@@ -152,9 +152,6 @@ class OsticketConfig extends Config {
         'pw_reset_window' =>    30,
         'enable_html_thread' => true,
         'allow_attachments' =>  true,
-        'allow_email_attachments' => true,
-        'allow_online_attachments' => true,
-        'allow_online_attachments_onlogin' => false,
         'name_format' =>        'full', # First Last
         'auto_claim_tickets'=>  true,
         'system_language' =>    'en_US',
@@ -385,6 +382,10 @@ class OsticketConfig extends Config {
         return ($email=$this->getDefaultEmail()) ? $email->getAddress() : null;
     }
 
+    function getDefaultTicketStatusId() {
+        return $this->get('default_ticket_status_id', 1);
+    }
+
     function getDefaultSLAId() {
         return $this->get('default_sla_id');
     }
@@ -444,16 +445,16 @@ class OsticketConfig extends Config {
     function setTopicSortMode($mode) {
         $modes = static::allTopicSortModes();
         if (!isset($modes[$mode]))
-            throw new InvalidArgumentException($mode
-                .': Unsupport help topic sort mode');
+            throw new InvalidArgumentException(sprintf(
+                '%s: Unsupported help topic sort mode', $mode));
 
         $this->update('help_topic_sort_mode', $mode);
     }
 
     static function allTopicSortModes() {
         return array(
-            'a' => 'Alphabetically',
-            'm' => 'Manually',
+            'a' => __('Alphabetically'),
+            'm' => __('Manually'),
         );
     }
 
@@ -624,8 +625,20 @@ class OsticketConfig extends Config {
         return true; //No longer an option...hint: big plans for headers coming!!
     }
 
-    function useRandomIds() {
-        return ($this->get('random_ticket_ids'));
+    function getDefaultSequence() {
+        if ($this->get('sequence_id'))
+            $sequence = Sequence::lookup($this->get('sequence_id'));
+        if (!$sequence)
+            $sequence = new RandomSequence();
+        return $sequence;
+    }
+    function getDefaultNumberFormat() {
+        return $this->get('number_format');
+    }
+    function getNewTicketNumber() {
+        $s = $this->getDefaultSequence();
+        return $s->next($this->getDefaultNumberFormat(),
+            array('Ticket', 'isTicketNumberUnique'));
     }
 
     /* autoresponders  & Alerts */
@@ -793,28 +806,10 @@ class OsticketConfig extends Config {
         return ($this->get('allow_attachments'));
     }
 
-    function allowOnlineAttachments() {
-        return ($this->allowAttachments() && $this->get('allow_online_attachments'));
-    }
-
-    function allowAttachmentsOnlogin() {
-        return ($this->allowOnlineAttachments() && $this->get('allow_online_attachments_onlogin'));
-    }
-
-    function allowEmailAttachments() {
-        return ($this->allowAttachments() && $this->get('allow_email_attachments'));
-    }
-
     function getSystemLanguage() {
         return $this->get('system_language');
     }
 
-    //TODO: change db field to allow_api_attachments - which will include  email/json/xml attachments
-    //       terminology changed on the UI
-    function allowAPIAttachments() {
-        return $this->allowEmailAttachments();
-    }
-
     /* Needed by upgrader on 1.6 and older releases upgrade - not not remove */
     function getUploadDir() {
         return $this->get('upload_dir');
@@ -859,7 +854,7 @@ class OsticketConfig extends Config {
                 return $this->updateKBSettings($vars, $errors);
                 break;
             default:
-                $errors['err']='Unknown setting option. Get technical support.';
+                $errors['err']=__('Unknown setting option. Get technical support.');
         }
 
         return false;
@@ -868,15 +863,15 @@ class OsticketConfig extends Config {
     function updateSystemSettings($vars, &$errors) {
 
         $f=array();
-        $f['helpdesk_url']=array('type'=>'string',   'required'=>1, 'error'=>'Helpdesk URl required');
-        $f['helpdesk_title']=array('type'=>'string',   'required'=>1, 'error'=>'Helpdesk title required');
-        $f['default_dept_id']=array('type'=>'int',   'required'=>1, 'error'=>'Default Dept. required');
+        $f['helpdesk_url']=array('type'=>'string',   'required'=>1, 'error'=>__('Helpdesk URL is required'));
+        $f['helpdesk_title']=array('type'=>'string',   'required'=>1, 'error'=>__('Helpdesk title is required'));
+        $f['default_dept_id']=array('type'=>'int',   'required'=>1, 'error'=>__('Default Department is required'));
         //Date & Time Options
-        $f['time_format']=array('type'=>'string',   'required'=>1, 'error'=>'Time format required');
-        $f['date_format']=array('type'=>'string',   'required'=>1, 'error'=>'Date format required');
-        $f['datetime_format']=array('type'=>'string',   'required'=>1, 'error'=>'Datetime format required');
-        $f['daydatetime_format']=array('type'=>'string',   'required'=>1, 'error'=>'Day, Datetime format required');
-        $f['default_timezone_id']=array('type'=>'int',   'required'=>1, 'error'=>'Default Timezone required');
+        $f['time_format']=array('type'=>'string',   'required'=>1, 'error'=>__('Time format is required'));
+        $f['date_format']=array('type'=>'string',   'required'=>1, 'error'=>__('Date format is required'));
+        $f['datetime_format']=array('type'=>'string',   'required'=>1, 'error'=>__('Datetime format is required'));
+        $f['daydatetime_format']=array('type'=>'string',   'required'=>1, 'error'=>__('Day, Datetime format is required'));
+        $f['default_timezone_id']=array('type'=>'int',   'required'=>1, 'error'=>__('Default Timezone is required'));
 
         if(!Validator::process($f, $vars, $errors) || $errors)
             return false;
@@ -904,7 +899,7 @@ class OsticketConfig extends Config {
         $f['staff_session_timeout']=array('type'=>'int',   'required'=>1, 'error'=>'Enter idle time in minutes');
         $f['client_session_timeout']=array('type'=>'int',   'required'=>1, 'error'=>'Enter idle time in minutes');
         $f['pw_reset_window']=array('type'=>'int', 'required'=>1, 'min'=>1,
-            'error'=>'Valid password reset window required');
+            'error'=>__('Valid password reset window required'));
 
 
         if(!Validator::process($f, $vars, $errors) || $errors)
@@ -929,46 +924,29 @@ class OsticketConfig extends Config {
 
     function updateTicketsSettings($vars, &$errors) {
         $f=array();
-        $f['default_sla_id']=array('type'=>'int',   'required'=>1, 'error'=>'Selection required');
-        $f['default_priority_id']=array('type'=>'int',   'required'=>1, 'error'=>'Selection required');
-        $f['max_open_tickets']=array('type'=>'int',   'required'=>1, 'error'=>'Enter valid numeric value');
-        $f['autolock_minutes']=array('type'=>'int',   'required'=>1, 'error'=>'Enter lock time in minutes');
+        $f['default_sla_id']=array('type'=>'int',   'required'=>1, 'error'=>__('Selection required'));
+        $f['default_ticket_status_id'] = array('type'=>'int', 'required'=>1, 'error'=>__('Selection required'));
+        $f['default_priority_id']=array('type'=>'int',   'required'=>1, 'error'=>__('Selection required'));
+        $f['max_open_tickets']=array('type'=>'int',   'required'=>1, 'error'=>__('Enter valid numeric value'));
+        $f['autolock_minutes']=array('type'=>'int',   'required'=>1, 'error'=>__('Enter lock time in minutes'));
 
 
         if($vars['enable_captcha']) {
             if (!extension_loaded('gd'))
-                $errors['enable_captcha']='The GD extension is required';
+                $errors['enable_captcha']=__('The GD extension is required');
             elseif(!function_exists('imagepng'))
-                $errors['enable_captcha']='PNG support required for Image Captcha';
-        }
-
-        if($vars['allow_attachments']) {
-
-            if(!ini_get('file_uploads'))
-                $errors['err']='The \'file_uploads\' directive is disabled in php.ini';
-
-            if(!is_numeric($vars['max_file_size']))
-                $errors['max_file_size']='Maximum file size required';
-
-            if(!$vars['allowed_filetypes'])
-                $errors['allowed_filetypes']='Allowed file extentions required';
-
-            if(!($maxfileuploads=ini_get('max_file_uploads')))
-                $maxfileuploads=DEFAULT_MAX_FILE_UPLOADS;
-
-            if(!$vars['max_user_file_uploads'] || $vars['max_user_file_uploads']>$maxfileuploads)
-                $errors['max_user_file_uploads']='Invalid selection. Must be less than '.$maxfileuploads;
-
-            if(!$vars['max_staff_file_uploads'] || $vars['max_staff_file_uploads']>$maxfileuploads)
-                $errors['max_staff_file_uploads']='Invalid selection. Must be less than '.$maxfileuploads;
+                $errors['enable_captcha']=__('PNG support is required for Image Captcha');
         }
 
         if ($vars['default_help_topic']
                 && ($T = Topic::lookup($vars['default_help_topic']))
                 && !$T->isActive()) {
-            $errors['default_help_topic'] = 'Default help topic must be set to active';
+            $errors['default_help_topic'] = __('Default help topic must be set to active');
         }
 
+        if (!preg_match('`(?!<\\\)#`', $vars['number_format']))
+            $errors['number_format'] = 'Ticket number format requires at least one hash character (#)';
+
         if(!Validator::process($f, $vars, $errors) || $errors)
             return false;
 
@@ -976,9 +954,11 @@ class OsticketConfig extends Config {
             $this->update('default_storage_bk', $vars['default_storage_bk']);
 
         return $this->updateAll(array(
-            'random_ticket_ids'=>$vars['random_ticket_ids'],
+            'number_format'=>$vars['number_format'] ?: '######',
+            'sequence_id'=>$vars['sequence_id'] ?: 0,
             'default_priority_id'=>$vars['default_priority_id'],
             'default_help_topic'=>$vars['default_help_topic'],
+            'default_ticket_status_id'=>$vars['default_ticket_status_id'],
             'default_sla_id'=>$vars['default_sla_id'],
             'max_open_tickets'=>$vars['max_open_tickets'],
             'autolock_minutes'=>$vars['autolock_minutes'],
@@ -990,32 +970,24 @@ class OsticketConfig extends Config {
             'hide_staff_name'=>isset($vars['hide_staff_name'])?1:0,
             'enable_html_thread'=>isset($vars['enable_html_thread'])?1:0,
             'allow_client_updates'=>isset($vars['allow_client_updates'])?1:0,
-            'allow_attachments'=>isset($vars['allow_attachments'])?1:0,
-            'allowed_filetypes'=>strtolower(preg_replace("/\n\r|\r\n|\n|\r/", '',trim($vars['allowed_filetypes']))),
             'max_file_size'=>$vars['max_file_size'],
-            'max_user_file_uploads'=>$vars['max_user_file_uploads'],
-            'max_staff_file_uploads'=>$vars['max_staff_file_uploads'],
             'email_attachments'=>isset($vars['email_attachments'])?1:0,
-            'allow_email_attachments'=>isset($vars['allow_email_attachments'])?1:0,
-            'allow_online_attachments'=>isset($vars['allow_online_attachments'])?1:0,
-            'allow_online_attachments_onlogin'=>isset($vars['allow_online_attachments_onlogin'])?1:0,
         ));
     }
 
 
     function updateEmailsSettings($vars, &$errors) {
-
         $f=array();
-        $f['default_template_id']=array('type'=>'int',   'required'=>1, 'error'=>'You must select template.');
-        $f['default_email_id']=array('type'=>'int',   'required'=>1, 'error'=>'Default email required');
-        $f['alert_email_id']=array('type'=>'int',   'required'=>1, 'error'=>'Selection required');
-        $f['admin_email']=array('type'=>'email',   'required'=>1, 'error'=>'System admin email required');
+        $f['default_template_id']=array('type'=>'int',   'required'=>1, 'error'=>__('You must select template'));
+        $f['default_email_id']=array('type'=>'int',   'required'=>1, 'error'=>__('Default email is required'));
+        $f['alert_email_id']=array('type'=>'int',   'required'=>1, 'error'=>__('Selection required'));
+        $f['admin_email']=array('type'=>'email',   'required'=>1, 'error'=>__('System admin email is required'));
 
         if($vars['strip_quoted_reply'] && !trim($vars['reply_separator']))
-            $errors['reply_separator']='Reply separator required to strip quoted reply.';
+            $errors['reply_separator']=__('Reply separator is required to strip quoted reply.');
 
         if($vars['admin_email'] && Email::getIdByEmail($vars['admin_email'])) //Make sure admin email is not also a system email.
-            $errors['admin_email']='Email already setup as system email';
+            $errors['admin_email']=__('Email already setup as system email');
 
         if(!Validator::process($f,$vars,$errors) || $errors)
             return false;
@@ -1066,7 +1038,7 @@ class OsticketConfig extends Config {
             elseif ($logo['error'])
                 $errors['logo'] = $logo['error'];
             elseif (!($id = AttachmentFile::uploadLogo($logo, $error)))
-                $errors['logo'] = 'Unable to upload logo image. '.$error;
+                $errors['logo'] = sprintf(__('Unable to upload logo image: %s'), $error);
         }
 
         $company = $ost->company;
@@ -1123,48 +1095,47 @@ class OsticketConfig extends Config {
 
     function updateAlertsSettings($vars, &$errors) {
 
-
        if($vars['ticket_alert_active']
                 && (!isset($vars['ticket_alert_admin'])
                     && !isset($vars['ticket_alert_dept_manager'])
                     && !isset($vars['ticket_alert_dept_members'])
                     && !isset($vars['ticket_alert_acct_manager']))) {
-            $errors['ticket_alert_active']='Select recipient(s)';
+            $errors['ticket_alert_active']=__('Select recipient(s)');
         }
         if($vars['message_alert_active']
                 && (!isset($vars['message_alert_laststaff'])
                     && !isset($vars['message_alert_assigned'])
                     && !isset($vars['message_alert_dept_manager'])
                     && !isset($vars['message_alert_acct_manager']))) {
-            $errors['message_alert_active']='Select recipient(s)';
+            $errors['message_alert_active']=__('Select recipient(s)');
         }
 
         if($vars['note_alert_active']
                 && (!isset($vars['note_alert_laststaff'])
                     && !isset($vars['note_alert_assigned'])
                     && !isset($vars['note_alert_dept_manager']))) {
-            $errors['note_alert_active']='Select recipient(s)';
+            $errors['note_alert_active']=__('Select recipient(s)');
         }
 
         if($vars['transfer_alert_active']
                 && (!isset($vars['transfer_alert_assigned'])
                     && !isset($vars['transfer_alert_dept_manager'])
                     && !isset($vars['transfer_alert_dept_members']))) {
-            $errors['transfer_alert_active']='Select recipient(s)';
+            $errors['transfer_alert_active']=__('Select recipient(s)');
         }
 
         if($vars['overdue_alert_active']
                 && (!isset($vars['overdue_alert_assigned'])
                     && !isset($vars['overdue_alert_dept_manager'])
                     && !isset($vars['overdue_alert_dept_members']))) {
-            $errors['overdue_alert_active']='Select recipient(s)';
+            $errors['overdue_alert_active']=__('Select recipient(s)');
         }
 
         if($vars['assigned_alert_active']
                 && (!isset($vars['assigned_alert_staff'])
                     && !isset($vars['assigned_alert_team_lead'])
                     && !isset($vars['assigned_alert_team_members']))) {
-            $errors['assigned_alert_active']='Select recipient(s)';
+            $errors['assigned_alert_active']=__('Select recipient(s)');
         }
 
         if($errors) return false;
diff --git a/include/class.cron.php b/include/class.cron.php
index 06d992cdcea53dcca5f02891014be3c6ce27a37a..af5a1476865f7cf11f40f1b39f195f11a1660f63 100644
--- a/include/class.cron.php
+++ b/include/class.cron.php
@@ -102,7 +102,8 @@ class Cron {
         self::PurgeDrafts();
         self::MaybeOptimizeTables();
 
-        Signal::send('cron', null);
+        $data = array('autocron'=>false);
+        Signal::send('cron', $data);
     }
 }
 ?>
diff --git a/include/class.dept.php b/include/class.dept.php
index 1dd57e0ddc64f0cb87add97d3bd00194c9e2685d..7abb372d1e9c16f75deb80b8ed60ee31a6a87c1b 100644
--- a/include/class.dept.php
+++ b/include/class.dept.php
@@ -13,6 +13,7 @@
 
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
+
 class Dept {
     var $id;
 
@@ -412,18 +413,18 @@ class Dept {
         global $cfg;
 
         if($id && $id!=$vars['id'])
-            $errors['err']='Missing or invalid Dept ID (internal error).';
+            $errors['err']=__('Missing or invalid Dept ID (internal error).');
 
         if(!$vars['name']) {
-            $errors['name']='Name required';
+            $errors['name']=__('Name required');
         } elseif(strlen($vars['name'])<4) {
-            $errors['name']='Name is too short.';
+            $errors['name']=__('Name is too short.');
         } elseif(($did=Dept::getIdByName($vars['name'])) && $did!=$id) {
-            $errors['name']='Department already exists';
+            $errors['name']=__('Department already exists');
         }
 
         if(!$vars['ispublic'] && $cfg && ($vars['id']==$cfg->getDefaultDeptId()))
-            $errors['ispublic']='System default department cannot be private';
+            $errors['ispublic']=__('System default department cannot be private');
 
         if($errors) return false;
 
@@ -447,7 +448,8 @@ class Dept {
             if(db_query($sql) && db_affected_rows())
                 return true;
 
-            $errors['err']='Unable to update '.Format::htmlchars($vars['name']).' Dept. Error occurred';
+            $errors['err']=sprintf(__('Unable to update %s.'), __('this department'))
+               .' '.__('Internal error occurred');
 
         } else {
             if (isset($vars['id']))
@@ -458,7 +460,8 @@ class Dept {
                 return $id;
 
 
-            $errors['err']='Unable to create department. Internal error';
+            $errors['err']=sprintf(__('Unable to create %s.'), __('this department'))
+               .' '.__('Internal error occurred');
 
         }
 
diff --git a/include/class.dispatcher.php b/include/class.dispatcher.php
index d586fd0ba77e010549d72196bcaec0a5f94c44d2..3490e78826d62287530f2fc23ba1a1bf4a15b462 100644
--- a/include/class.dispatcher.php
+++ b/include/class.dispatcher.php
@@ -38,7 +38,7 @@ class Dispatcher {
                 return $matcher->dispatch($url, $args);
             }
         }
-        Http::response(400, "URL not supported");
+        Http::response(400, __("URL not supported"));
     }
     /**
      * Returns the url for the given function and arguments (arguments
@@ -140,7 +140,7 @@ class UrlMatcher {
         }
 
         if (!is_callable($func))
-            Http::response(500, 'Dispatcher compile error. Function not callable');
+            Http::response(500, __('Dispatcher compile error. Function not callable'));
 
         return call_user_func_array($func, $args);
     }
diff --git a/include/class.draft.php b/include/class.draft.php
index cc6ad1fef9a2d7291c407ed61149dc690aa1eb4f..cdb5d43f440081d3e047993dd0df006cf23c39d6 100644
--- a/include/class.draft.php
+++ b/include/class.draft.php
@@ -33,7 +33,9 @@ class Draft {
         if (preg_match_all('/"cid:([\\w.-]{32})"/', $body, $matches)) {
             foreach ($matches[1] as $hash) {
                 if ($file_id = AttachmentFile::getIdByHash($hash))
-                    $attachments[] = $file_id;
+                    $attachments[] = array(
+                            'id' => $file_id,
+                            'inline' => true);
             }
         }
         return $attachments;
diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php
index 61142fdab28a39b32a07d317dc8f441d951f2d16..1b31e882d75025c248c48969627a2af75d617471 100644
--- a/include/class.dynamic_forms.php
+++ b/include/class.dynamic_forms.php
@@ -18,6 +18,7 @@
 **********************************************************************/
 require_once(INCLUDE_DIR . 'class.orm.php');
 require_once(INCLUDE_DIR . 'class.forms.php');
+require_once(INCLUDE_DIR . 'class.list.php');
 require_once(INCLUDE_DIR . 'class.filter.php');
 require_once(INCLUDE_DIR . 'class.signal.php');
 
@@ -46,17 +47,24 @@ class DynamicForm extends VerySimpleModel {
     var $_dfields;
 
     function getFields($cache=true) {
-        if (!isset($this->_fields) || !$cache) {
-            $this->_fields = array();
+        if (!$cache)
+            $fields = false;
+        else
+            $fields = &$this->_fields;
+
+        if (!$fields) {
+            $fields = new ListObject();
             foreach ($this->getDynamicFields() as $f)
-                // TODO: Index by field name or id
-                $this->_fields[$f->get('id')] = $f->getImpl($f);
+                $fields->append($f->getImpl($f));
         }
-        return $this->_fields;
+
+        return $fields;
     }
 
     function getDynamicFields() {
-        if (!$this->_dfields && isset($this->id)) {
+        if (!isset($this->id))
+            return array();
+        elseif (!$this->_dfields) {
             $this->_dfields = DynamicFormField::objects()
                 ->filter(array('form_id'=>$this->id))
                 ->all();
@@ -70,7 +78,7 @@ class DynamicForm extends VerySimpleModel {
     function __call($what, $args) {
         $delegate = array($this->getForm(), $what);
         if (!is_callable($delegate))
-            throw new Exception($what.': Call to non-existing function');
+            throw new Exception(sprintf(__('%s: Call to non-existing function'), $what));
         return call_user_func_array($delegate, $args);
     }
 
@@ -190,16 +198,19 @@ class DynamicForm extends VerySimpleModel {
             if (!$impl->hasData() || $impl->isPresentationOnly())
                 continue;
 
+            $id = $f->get('id');
             $name = ($f->get('name')) ? $f->get('name')
-                : 'field_'.$f->get('id');
+                : 'field_'.$id;
 
-            $fields[] = sprintf(
-                'MAX(IF(field.name=\'%1$s\',ans.value,NULL)) as `%1$s`',
-                $name);
-            if ($impl->hasIdValue()) {
+            if ($impl instanceof ChoiceField || $impl instanceof SelectionField) {
                 $fields[] = sprintf(
-                    'MAX(IF(field.name=\'%1$s\',ans.value_id,NULL)) as `%1$s_id`',
-                    $name);
+                    'MAX(CASE WHEN field.id=\'%1$s\' THEN REPLACE(REPLACE(REPLACE(REPLACE(coalesce(ans.value_id, ans.value), \'{\', \'\'), \'}\', \'\'), \'"\', \'\'), \':\', \',\') ELSE NULL END) as `%2$s`',
+                    $id, $name);
+            }
+            else {
+                $fields[] = sprintf(
+                    'MAX(IF(field.id=\'%1$s\',coalesce(ans.value_id, ans.value),NULL)) as `%2$s`',
+                    $id, $name);
             }
         }
         return $fields;
@@ -237,16 +248,16 @@ class UserForm extends DynamicForm {
         return static::$instance;
     }
 }
-Filter::addSupportedMatches('User Data', function() {
+Filter::addSupportedMatches(/* @trans */ 'User Data', function() {
     $matches = array();
     foreach (UserForm::getInstance()->getFields() as $f) {
         if (!$f->hasData())
             continue;
-        $matches['field.'.$f->get('id')] = 'User / '.$f->getLabel();
-        if (($fi = $f->getImpl()) instanceof SelectionField) {
-            foreach ($fi->getList()->getProperties() as $p) {
+        $matches['field.'.$f->get('id')] = __('User').' / '.$f->getLabel();
+        if (($fi = $f->getImpl()) && $fi->hasSubFields()) {
+            foreach ($fi->getSubFields() as $p) {
                 $matches['field.'.$f->get('id').'.'.$p->get('id')]
-                    = 'User / '.$f->getLabel().' / '.$p->getLabel();
+                    = __('User').' / '.$f->getLabel().' / '.$p->getLabel();
             }
         }
     }
@@ -315,10 +326,8 @@ class TicketForm extends DynamicForm {
         $f = $answer->getField();
         $name = $f->get('name') ? $f->get('name')
             : 'field_'.$f->get('id');
-        $ids = $f->hasIdValue();
-        $fields = sprintf('`%s`=', $name) . db_input($answer->get('value'));
-        if ($f->hasIdValue())
-            $fields .= sprintf(',`%s_id`=', $name) . db_input($answer->getIdValue());
+        $fields = sprintf('`%s`=', $name) . db_input(
+            implode(',', $answer->getSearchKeys()));
         $sql = 'INSERT INTO `'.TABLE_PREFIX.'ticket__cdata` SET '.$fields
             .', `ticket_id`='.db_input($answer->getEntry()->get('object_id'))
             .' ON DUPLICATE KEY UPDATE '.$fields;
@@ -327,16 +336,16 @@ class TicketForm extends DynamicForm {
     }
 }
 // Add fields from the standard ticket form to the ticket filterable fields
-Filter::addSupportedMatches('Ticket Data', function() {
+Filter::addSupportedMatches(/* @trans */ 'Ticket Data', function() {
     $matches = array();
     foreach (TicketForm::getInstance()->getFields() as $f) {
         if (!$f->hasData())
             continue;
-        $matches['field.'.$f->get('id')] = 'Ticket / '.$f->getLabel();
-        if (($fi = $f->getImpl()) instanceof SelectionField) {
-            foreach ($fi->getList()->getProperties() as $p) {
+        $matches['field.'.$f->get('id')] = __('Ticket').' / '.$f->getLabel();
+        if (($fi = $f->getImpl()) && $fi->hasSubFields()) {
+            foreach ($fi->getSubFields() as $p) {
                 $matches['field.'.$f->get('id').'.'.$p->get('id')]
-                    = 'Ticket / '.$f->getLabel().' / '.$p->getLabel();
+                    = __('Ticket').' / '.$f->getLabel().' / '.$p->getLabel();
             }
         }
     }
@@ -375,8 +384,8 @@ Filter::addSupportedMatches(/* trans */ 'Custom Forms', function() {
             if (!$f->hasData())
                 continue;
             $matches['field.'.$f->get('id')] = $form->getTitle().' / '.$f->getLabel();
-            if (($fi = $f->getImpl()) instanceof SelectionField) {
-                foreach ($fi->getList()->getProperties() as $p) {
+            if (($fi = $f->getImpl()) && $fi->hasSubFields()) {
+                foreach ($fi->getSubFields() as $p) {
                     $matches['field.'.$f->get('id').'.'.$p->get('id')]
                         = $form->getTitle().' / '.$f->getLabel().' / '.$p->getLabel();
                 }
@@ -404,6 +413,15 @@ class DynamicFormField extends VerySimpleModel {
 
     var $_field;
 
+    const REQUIRE_NOBODY = 0;
+    const REQUIRE_EVERYONE = 1;
+    const REQUIRE_ENDUSER = 2;
+    const REQUIRE_AGENT = 3;
+
+    const VISIBLE_EVERYONE = 0;
+    const VISIBLE_AGENTONLY = 1;
+    const VISIBLE_ENDUSERONLY = 2;
+
     // Multiple inheritance -- delegate to FormField
     function __call($what, $args) {
         return call_user_func_array(
@@ -442,8 +460,8 @@ class DynamicFormField extends VerySimpleModel {
      */
     function setConfiguration(&$errors=array()) {
         $config = array();
-        foreach ($this->getConfigurationForm() as $name=>$field) {
-            $config[$name] = $field->getClean();
+        foreach ($this->getConfigurationForm($_POST)->getFields() as $name=>$field) {
+            $config[$name] = $field->to_php($field->getClean());
             $errors = array_merge($errors, $field->errors());
         }
         if (count($errors) === 0)
@@ -453,7 +471,7 @@ class DynamicFormField extends VerySimpleModel {
     }
 
     function isDeletable() {
-        return ($this->get('edit_mask') & 1) == 0;
+        return (($this->get('edit_mask') & 1) == 0);
     }
     function isNameForced() {
         return $this->get('edit_mask') & 2;
@@ -465,6 +483,89 @@ class DynamicFormField extends VerySimpleModel {
         return $this->get('edit_mask') & 8;
     }
 
+    function  isChangeable() {
+        return (($this->get('edit_mask') & 16) == 0);
+    }
+
+    function  isEditable() {
+        return (($this->get('edit_mask') & 32) == 0);
+    }
+
+    function allRequirementModes() {
+        return array(
+            'a' => array('desc' => __('Optional'),
+                'private' => self::VISIBLE_EVERYONE, 'required' => self::REQUIRE_NOBODY),
+            'b' => array('desc' => __('Required'),
+                'private' => self::VISIBLE_EVERYONE, 'required' => self::REQUIRE_EVERYONE),
+            'c' => array('desc' => __('Required for EndUsers'),
+                'private' => self::VISIBLE_EVERYONE, 'required' => self::REQUIRE_ENDUSER),
+            'd' => array('desc' => __('Required for Agents'),
+                'private' => self::VISIBLE_EVERYONE, 'required' => self::REQUIRE_AGENT),
+            'e' => array('desc' => __('Internal, Optional'),
+                'private' => self::VISIBLE_AGENTONLY, 'required' => self::REQUIRE_NOBODY),
+            'f' => array('desc' => __('Internal, Required'),
+                'private' => self::VISIBLE_AGENTONLY, 'required' => self::REQUIRE_EVERYONE),
+            'g' => array('desc' => __('For EndUsers Only'),
+                'private' => self::VISIBLE_ENDUSERONLY, 'required' => self::REQUIRE_ENDUSER),
+        );
+    }
+
+    function getAllRequirementModes() {
+        $modes = static::allRequirementModes();
+        if ($this->isPrivacyForced()) {
+            // Required to be internal
+            foreach ($modes as $m=>$info) {
+                if ($info['private'] != $this->get('private'))
+                    unset($modes[$m]);
+            }
+        }
+
+        if ($this->isRequirementForced()) {
+            // Required to be required
+            foreach ($modes as $m=>$info) {
+                if ($info['required'] != $this->get('required'))
+                    unset($modes[$m]);
+            }
+        }
+        return $modes;
+    }
+
+    function getRequirementMode() {
+        foreach ($this->getAllRequirementModes() as $m=>$info) {
+            if ($this->get('private') == $info['private']
+                    && $this->get('required') == $info['required'])
+                return $m;
+        }
+        return false;
+    }
+
+    function setRequirementMode($mode) {
+        $modes = $this->getAllRequirementModes();
+        if (!isset($modes[$mode]))
+            return false;
+
+        $info = $modes[$mode];
+        $this->set('required', $info['required']);
+        $this->set('private', $info['private']);
+    }
+
+    function isRequiredForStaff() {
+        return in_array($this->get('required'),
+            array(self::REQUIRE_EVERYONE, self::REQUIRE_AGENT));
+    }
+    function isRequiredForUsers() {
+        return in_array($this->get('required'),
+            array(self::REQUIRE_EVERYONE, self::REQUIRE_ENDUSER));
+    }
+    function isVisibleToStaff() {
+        return in_array($this->get('private'),
+            array(self::VISIBLE_EVERYONE, self::VISIBLE_AGENTONLY));
+    }
+    function isVisibleToUsers() {
+        return in_array($this->get('private'),
+            array(self::VISIBLE_EVERYONE, self::VISIBLE_ENDUSERONLY));
+    }
+
     /**
      * Used when updating the form via the admin panel. This represents
      * validation on the form field template, not data entered into a form
@@ -475,10 +576,17 @@ class DynamicFormField extends VerySimpleModel {
             return false;
         if (!$this->get('label'))
             $this->addError(
-                "Label is required for custom form fields", "label");
+                __("Label is required for custom form fields"), "label");
         if ($this->get('required') && !$this->get('name'))
             $this->addError(
-                "Variable name is required for required fields", "name");
+                __("Variable name is required for required fields"
+                /* `required` is a visibility setting fields */
+                /* `variable` is used for automation. Internally it's called `name` */
+                ), "name");
+        if (preg_match('/[.{}\'"`; ]/u', $this->get('name')))
+            $this->addError(__(
+                'Invalid character in variable name. Please use letters and numbers only.'
+            ), 'name');
         return count($this->errors()) == 0;
     }
 
@@ -626,19 +734,25 @@ class DynamicFormEntry extends VerySimpleModel {
         if (!is_array($this->_errors)) {
             $this->_errors = array();
             $this->getClean();
-            foreach ($this->getFields() as $field)
+            foreach ($this->getFields() as $field) {
                 if ($field->errors() && (!$filter || $filter($field)))
                     $this->_errors[$field->get('id')] = $field->errors();
+            }
         }
         return !$this->_errors;
     }
 
     function isValidForClient() {
-
         $filter = function($f) {
-            return !$f->get('private');
+            return $f->isVisibleToUsers();
         };
+        return $this->isValid($filter);
+    }
 
+    function isValidForStaff() {
+        $filter = function($f) {
+            return $f->isVisibleToStaff();
+        };
         return $this->isValid($filter);
     }
 
@@ -697,9 +811,14 @@ class DynamicFormEntry extends VerySimpleModel {
 
     function forTicket($ticket_id, $force=false) {
         static $entries = array();
-        if (!isset($entries[$ticket_id]) || $force)
-            $entries[$ticket_id] = DynamicFormEntry::objects()
+        if (!isset($entries[$ticket_id]) || $force) {
+            $stuff = DynamicFormEntry::objects()
                 ->filter(array('object_id'=>$ticket_id, 'object_type'=>'T'));
+            // If forced, don't cache the result
+            if ($force)
+                return $stuff;
+            $entries[$ticket_id] = &$stuff;
+        }
         return $entries[$ticket_id];
     }
     function setTicketId($ticket_id) {
@@ -806,6 +925,9 @@ class DynamicFormEntry extends VerySimpleModel {
                     && in_array($field->get('name'), array('name')))
                 continue;
 
+            // Set the entry ID here so that $field->getClean() can use the
+            // entry-id if necessary
+            $a->set('entry_id', $this->get('id'));
             $val = $field->to_database($field->getClean());
             if (is_array($val)) {
                 $a->set('value', $val[0]);
@@ -813,7 +935,6 @@ class DynamicFormEntry extends VerySimpleModel {
             }
             else
                 $a->set('value', $val);
-            $a->set('entry_id', $this->get('id'));
             // Don't save answers for presentation-only fields
             if ($field->hasData() && !$field->isPresentationOnly())
                 $a->save();
@@ -912,6 +1033,25 @@ class DynamicFormEntryAnswer extends VerySimpleModel {
         return $this->getField()->display($this->getValue());
     }
 
+    function getSearchable($include_label=false) {
+        if ($include_label)
+            $label = Format::searchable($this->getField()->getLabel()) . " ";
+        return sprintf("%s%s", $label,
+            $this->getField()->searchable($this->getValue())
+        );
+    }
+
+    function getSearchKeys() {
+        $val = $this->getField()->to_php(
+            $this->get('value'), $this->get('value_id'));
+        if (is_array($val))
+            return array_keys($val);
+        elseif (is_object($val) && method_exists($val, 'getId'))
+            return array($val->getId());
+
+        return array($val);
+    }
+
     function asVar() {
         return (is_object($this->getValue()))
             ? $this->getValue() : $this->toString();
@@ -928,343 +1068,183 @@ class DynamicFormEntryAnswer extends VerySimpleModel {
     }
 }
 
-/**
- * Dynamic lists are used to represent list of arbitrary data that can be
- * used as dropdown or typeahead selections in dynamic forms. This model
- * defines a list. The individual items are stored in the DynamicListItem
- * model.
- */
-class DynamicList extends VerySimpleModel {
-
-    static $meta = array(
-        'table' => LIST_TABLE,
-        'ordering' => array('name'),
-        'pk' => array('id'),
-    );
-
-    var $_items;
-    var $_form;
-
-    function getSortModes() {
-        return array(
-            'Alpha'     => 'Alphabetical',
-            '-Alpha'    => 'Alphabetical (Reversed)',
-            'SortCol'   => 'Manually Sorted'
-        );
-    }
-
-    function getListOrderBy() {
-        switch ($this->sort_mode) {
-            case 'Alpha':   return 'value';
-            case '-Alpha':  return '-value';
-            case 'SortCol': return 'sort';
-        }
-    }
-
-    function getPluralName() {
-        if ($name = $this->get('name_plural'))
-            return $name;
-        else
-            return $this->get('name') . 's';
-    }
-
-    function getAllItems() {
-         return DynamicListItem::objects()->filter(
-                array('list_id'=>$this->get('id')))
-                ->order_by($this->getListOrderBy());
-    }
-
-    function getItems($limit=false, $offset=false) {
-        if (!$this->_items) {
-            $this->_items = DynamicListItem::objects()->filter(
-                array('list_id'=>$this->get('id'),
-                      'status__hasbit'=>DynamicListItem::ENABLED))
-                ->order_by($this->getListOrderBy());
-            if ($limit)
-                $this->_items->limit($limit);
-            if ($offset)
-                $this->_items->offset($offset);
-        }
-        return $this->_items;
-    }
-
-    function getItemCount() {
-        return DynamicListItem::objects()->filter(array('list_id'=>$this->id))
-            ->count();
-    }
-
-    function getConfigurationForm() {
-        if (!$this->_form) {
-            $this->_form = DynamicForm::lookup(
-                array('type'=>'L'.$this->get('id')));
-        }
-        return $this->_form;
-    }
-
-    function getProperties() {
-        if ($f = $this->getForm())
-            return $f->getFields();
-        return array();
-    }
-
-    function getForm() {
-        return $this->getConfigurationForm();
-    }
-
-    function save($refetch=false) {
-        if (count($this->dirty))
-            $this->set('updated', new SqlFunction('NOW'));
-        if (isset($this->dirty['notes']))
-            $this->notes = Format::sanitize($this->notes);
-        return parent::save($refetch);
-    }
-
-    function delete() {
-        $fields = DynamicFormField::objects()->filter(array(
-            'type'=>'list-'.$this->id))->count();
-        if ($fields == 0)
-            return parent::delete();
-        else
-            // Refuse to delete lists that are in use by fields
-            return false;
-    }
-
-    static function create($ht=false) {
-        $inst = parent::create($ht);
-        $inst->set('created', new SqlFunction('NOW'));
-        return $inst;
-    }
-
-    static function getSelections() {
-        $selections = array();
-        foreach (DynamicList::objects() as $list) {
-            $selections['list-'.$list->id] =
-                array($list->getPluralName(),
-                    SelectionField, $list->get('id'));
-        }
-        return $selections;
-    }
-}
-FormField::addFieldTypes('Custom Lists', array('DynamicList', 'getSelections'));
-
-/**
- * Represents a single item in a dynamic list
- *
- * Fields:
- * value - (char * 255) Actual list item content
- * extra - (char * 255) Other values that represent the same item in the
- *      list, such as an abbreviation. In practice, should be a
- *      space-separated list of tokens which should hit this list item in a
- *      search
- * sort - (int) If sorting by this field, represents the numeric sort order
- *      that this item should come in the dropdown list
- */
-class DynamicListItem extends VerySimpleModel {
-
-    static $meta = array(
-        'table' => LIST_ITEM_TABLE,
-        'pk' => array('id'),
-        'joins' => array(
-            'list' => array(
-                'null' => true,
-                'constraint' => array('list_id' => 'DynamicList.id'),
-            ),
-        ),
-    );
-
-    var $_config;
-    var $_form;
-
-    const ENABLED               = 0x0001;
+class SelectionField extends FormField {
+    static $widget = 'ChoicesWidget';
 
-    protected function hasStatus($flag) {
-        return 0 !== ($this->get('status') & $flag);
+    function getListId() {
+        list(,$list_id) = explode('-', $this->get('type'));
+        return $list_id ?: $this->get('list_id');
     }
 
-    protected function clearStatus($flag) {
-        return $this->set('status', $this->get('status') & ~$flag);
-    }
+    function getList() {
+        if (!$this->_list)
+            $this->_list = DynamicList::lookup($this->getListId());
 
-    protected function setStatus($flag) {
-        return $this->set('status', $this->get('status') | $flag);
+        return $this->_list;
     }
 
-    function isEnabled() {
-        return $this->hasStatus(self::ENABLED);
+    function getWidget() {
+        $config = $this->getConfiguration();
+        $widgetClass = false;
+        if ($config['widget'] == 'typeahead')
+            $widgetClass = 'TypeaheadSelectionWidget';
+        return parent::getWidget($widgetClass);
     }
 
-    function enable() {
-        $this->setStatus(self::ENABLED);
-    }
-    function disable() {
-        $this->clearStatus(self::ENABLED);
-    }
+    function parse($value) {
 
-    function getConfiguration() {
-        if (!$this->_config) {
-            $this->_config = $this->get('properties');
-            if (is_string($this->_config))
-                $this->_config = JsonDataParser::parse($this->_config);
-            elseif (!$this->_config)
-                $this->_config = array();
-        }
-        return $this->_config;
-    }
+        if (!($list=$this->getList()))
+            return null;
 
-    function getFilterData() {
-        $raw = $this->getConfiguration();
-        $props = array();
-        if ($form = $this->getConfigurationForm()) {
-            foreach ($form->getFields() as $field) {
-                $tag = $field->get('id');
-                if (isset($raw[$tag]))
-                    $props[".$tag"] = $field->toString($raw[$tag]);
+        $config = $this->getConfiguration();
+        $choices = $this->getChoices();
+        $selection = array();
+        if ($value && is_array($value)) {
+            foreach ($value as $k=>$v) {
+                if (($i=$list->getItem((int) $k)))
+                    $selection[$i->getId()] = $i->getValue();
+                elseif (isset($choices[$v]))
+                    $selection[$v] = $choices[$v];
             }
         }
-        return $props;
-    }
-
-    function setConfiguration(&$errors=array()) {
-        $config = array();
-        foreach ($this->getConfigurationForm()->getFields() as $field) {
-            $val = $field->to_database($field->getClean());
-            $config[$field->get('id')] = is_array($val) ? $val[1] : $val;
-            $errors = array_merge($errors, $field->errors());
-        }
-        if (count($errors) === 0)
-            $this->set('properties', JsonDataEncoder::encode($config));
-
-        return count($errors) === 0;
-    }
 
-    function getConfigurationForm() {
-        if (!$this->_form) {
-            $this->_form = DynamicForm::lookup(
-                array('type'=>'L'.$this->get('list_id')));
-        }
-        return $this->_form;
+        return $selection;
     }
 
-    function getVar($name) {
-        $config = $this->getConfiguration();
-        $name = mb_strtolower($name);
-        foreach ($this->getConfigurationForm()->getFields() as $field) {
-            if (mb_strtolower($field->get('name')) == $name)
-                return $config[$field->get('id')];
+    function to_database($value) {
+        if (is_array($value)) {
+            reset($value);
         }
-    }
-
-    function toString() {
-        return $this->get('value');
-    }
-
-    function __toString() {
-        return $this->toString();
-    }
-
-    function delete() {
-        # Don't really delete, just unset the list_id to un-associate it with
-        # the list
-        $this->set('list_id', null);
-        return $this->save();
-    }
-}
-
-class SelectionField extends FormField {
-    static $widget = 'SelectionWidget';
-
-    function getListId() {
-        list(,$list_id) = explode('-', $this->get('type'));
-        return $list_id;
-    }
-
-    function getList() {
-        if (!$this->_list)
-            $this->_list = DynamicList::lookup($this->getListId());
-        return $this->_list;
-    }
+        if ($value && is_array($value))
+            $value = JsonDataEncoder::encode($value);
 
-    function parse($value) {
-        $config = $this->getConfiguration();
-        if (is_int($value))
-            $val = $this->to_php($this->getWidget()->getEnteredValue(), (int) $value);
-        elseif (!$config['typeahead'])
-            $val = $this->to_php(null, (int) $value);
-        if (!$val)
-            $val = $this->to_php($value);
-        return $val;
+        return $value;
     }
 
     function to_php($value, $id=false) {
-        if ($value === null && $id === null)
-            return null;
-        if ($value instanceof DynamicListItem)
-            $item = $value;
-        elseif ($id && is_int($id))
-            $item = DynamicListItem::lookup($id);
-        # Attempt item lookup by name too
-        if (!$item || ($value !== null && $value != $item->get('value'))) {
-            $item = DynamicListItem::lookup(array(
-                'value'=>$value,
-                'list_id'=>$this->getListId()));
+        $value = ($value && !is_array($value))
+            ? JsonDataParser::parse($value) : $value;
+        if (!is_array($value)) {
+            $choices = $this->getChoices();
+            if (isset($choices[$value]))
+                $value = $choices[$value];
         }
-        return ($item) ? $item : $value;
+        // Don't set the ID here as multiselect prevents using exactly one
+        // ID value. Instead, stick with the JSON value only.
+        return $value;
     }
 
-    function hasIdValue() {
-        return true;
+    function hasSubFields() {
+        return $this->getList()->getForm();
     }
-
-    function to_database($item) {
-        if ($item instanceof DynamicListItem)
-            return array($item->value, $item->id);
-        return null;
+    function getSubFields() {
+        $form = $this->getList()->getForm();
+        if ($form)
+            return $form->getFields();
+        return array();
     }
 
-    function toString($item) {
-        return ($item instanceof DynamicListItem)
-            ? $item->toString() : (string) $item;
+    function toString($items) {
+        return ($items && is_array($items))
+            ? implode(', ', $items) : (string) $items;
     }
 
-    function validateEntry($item) {
-        $config = $this->getConfiguration();
-        parent::validateEntry($item);
-        if ($item && !$item instanceof DynamicListItem)
-            $this->_errors[] = 'Select a value from the list';
-        elseif ($item && $config['typeahead']
-                && $this->getWidget()->getEnteredValue() != $item->get('value'))
-            $this->_errors[] = 'Select a value from the list';
+    function validateEntry($entry) {
+        parent::validateEntry($entry);
+        if (!$this->errors()) {
+            $config = $this->getConfiguration();
+            if ($config['typeahead']
+                    && ($entered = $this->getWidget()->getEnteredValue())
+                    && !in_array($entered, $entry))
+                $this->_errors[] = __('Select a value from the list');
+        }
     }
 
     function getConfigurationOptions() {
         return array(
-            'typeahead' => new ChoiceField(array(
-                'id'=>1, 'label'=>'Widget', 'required'=>false,
-                'default'=>false,
-                'choices'=>array(false=>'Drop Down', true=>'Typeahead'),
-                'hint'=>'Typeahead will work better for large lists'
+            'widget' => new ChoiceField(array(
+                'id'=>1,
+                'label'=>__('Widget'),
+                'required'=>false, 'default' => 'dropdown',
+                'choices'=>array(
+                    'dropdown' => __('Drop Down'),
+                    'typeahead' =>__('Typeahead'),
+                ),
+                'configuration'=>array(
+                    'multiselect' => false,
+                ),
+                'hint'=>__('Typeahead will work better for large lists')
+            )),
+            'multiselect' => new BooleanField(array(
+                'id'=>2,
+                'label'=>__(/* Type of widget allowing multiple selections */ 'Multiselect'),
+                'required'=>false, 'default'=>false,
+                'configuration'=>array(
+                    'desc'=>__('Allow multiple selections')),
+                'visibility' => new VisibilityConstraint(
+                    new Q(array('widget__eq'=>'dropdown')),
+                    VisibilityConstraint::HIDDEN
+                ),
             )),
             'prompt' => new TextboxField(array(
-                'id'=>2, 'label'=>'Prompt', 'required'=>false, 'default'=>'',
-                'hint'=>'Leading text shown before a value is selected',
+                'id'=>3,
+                'label'=>__('Prompt'), 'required'=>false, 'default'=>'',
+                'hint'=>__('Leading text shown before a value is selected'),
                 'configuration'=>array('size'=>40, 'length'=>40),
             )),
+            'default' => new SelectionField(array(
+                'id'=>4, 'label'=>__('Default'), 'required'=>false, 'default'=>'',
+                'list_id'=>$this->getListId(),
+                'configuration' => array('prompt'=>__('Select a Default')),
+            )),
         );
     }
 
-    function getChoices() {
-        if (!$this->_choices) {
+    function getConfiguration() {
+
+        $config = parent::getConfiguration();
+        if ($config['widget'])
+            $config['typeahead'] = $config['widget'] == 'typeahead';
+
+        //Typeahed doesn't support multiselect for now  TODO: Add!
+        if ($config['typeahead'])
+            $config['multiselect'] = false;
+
+        return $config;
+    }
+
+    function getChoices($verbose=false) {
+        if (!$this->_choices || $verbose) {
             $this->_choices = array();
             foreach ($this->getList()->getItems() as $i)
-                $this->_choices[$i->get('id')] = $i->get('value');
-            if ($this->value && !isset($this->_choices[$this->value])) {
-                if ($v = DynamicListItem::lookup($this->value))
-                    $this->_choices[$v->get('id')] = $v->get('value').' (Disabled)';
+                $this->_choices[$i->getId()] = $i->getValue();
+
+            // Retired old selections
+            $values = ($a=$this->getAnswer()) ? $a->getValue() : array();
+            if ($values && is_array($values)) {
+                foreach ($values as $k => $v) {
+                    if (!isset($this->_choices[$k])) {
+                        if ($verbose) $v .= ' '.__('(retired)');
+                        $this->_choices[$k] = $v;
+                    }
+                }
             }
         }
         return $this->_choices;
     }
 
+    function getChoice($value) {
+        $choices = $this->getChoices();
+        if ($value && is_array($value)) {
+            $selection = $value;
+        } elseif (isset($choices[$value]))
+            $selection[] = $choices[$value];
+        elseif ($this->get('default'))
+            $selection[] = $choices[$this->get('default')];
+
+        return $selection;
+    }
+
     function export($value) {
         if ($value && is_numeric($value)
                 && ($item = DynamicListItem::lookup($value)))
@@ -1281,46 +1261,53 @@ class SelectionField extends FormField {
     }
 }
 
-class SelectionWidget extends ChoicesWidget {
-    function render($mode=false) {
+class TypeaheadSelectionWidget extends ChoicesWidget {
+    function render($how) {
+        if ($how == 'search')
+            return parent::render($how);
+
+        $name = $this->getEnteredValue();
         $config = $this->field->getConfiguration();
-        $value = false;
-        if ($this->value instanceof DynamicListItem) {
-            // Loaded from database
-            $value = $this->value->get('id');
-            $name = $this->value->get('value');
-        } elseif ($this->value) {
-            // Loaded from POST
-            $value = $this->value;
-            $name = $this->getEnteredValue();
+        if (is_array($this->value)) {
+            $name = $name ?: current($this->value);
+            $value = key($this->value);
         }
-        if (!$config['typeahead'] || $mode=='search') {
-            $this->value = $value;
-            return parent::render($mode);
+        else {
+            // Pull configured default (if configured)
+            $def_key = $this->field->get('default');
+            if (!$def_key && $config['default'])
+                $def_key = $config['default'];
+            if (is_array($def_key))
+                $name = current($def_key);
         }
 
         $source = array();
         foreach ($this->field->getList()->getItems() as $i)
             $source[] = array(
-                'value' => $i->get('value'), 'id' => $i->get('id'),
-                'info' => $i->get('value')." -- ".$i->get('extra'),
+                'value' => $i->getValue(), 'id' => $i->getId(),
+                'info' => sprintf('%s %s',
+                    $i->getValue(),
+                    (($extra= $i->getAbbrev()) ? "-- $extra" : '')),
             );
         ?>
         <span style="display:inline-block">
-        <input type="text" size="30" name="<?php echo $this->name; ?>"
-            id="<?php echo $this->name; ?>" value="<?php echo $name; ?>"
+        <input type="text" size="30" name="<?php echo $this->name; ?>_name"
+            id="<?php echo $this->name; ?>" value="<?php echo Format::htmlchars($name); ?>"
             placeholder="<?php echo $config['prompt'];
             ?>" autocomplete="off" />
         <input type="hidden" name="<?php echo $this->name;
-            ?>_id" id="<?php echo $this->name; ?>_id" value="<?php echo $value; ?>"/>
+            ?>[<?php echo $value; ?>]" id="<?php echo $this->name;
+            ?>_id" value="<?php echo Format::htmlchars($name); ?>"/>
         <script type="text/javascript">
         $(function() {
             $('input#<?php echo $this->name; ?>').typeahead({
                 source: <?php echo JsonDataEncoder::encode($source); ?>,
                 property: 'info',
                 onselect: function(item) {
-                    $('input#<?php echo $this->name; ?>').val(item['value'])
-                    $('input#<?php echo $this->name; ?>_id').val(item['id'])
+                    $('input#<?php echo $this->name; ?>_name').val(item['value'])
+                    $('input#<?php echo $this->name; ?>_id')
+                      .attr('name', '<?php echo $this->name; ?>[' + item['id'] + ']')
+                      .val(item['value']);
                 }
             });
         });
@@ -1331,14 +1318,16 @@ class SelectionWidget extends ChoicesWidget {
 
     function getValue() {
         $data = $this->field->getSource();
-        // Search for HTML form name first
-        if (isset($data[$this->name.'_id']))
-            return (int) $data[$this->name.'_id'];
+        if (isset($data[$this->name]))
+            return $data[$this->name];
         return parent::getValue();
     }
 
     function getEnteredValue() {
         // Used to verify typeahead fields
+        $data = $this->field->getSource();
+        if (isset($data[$this->name.'_name']))
+            return trim($data[$this->name.'_name']);
         return parent::getValue();
     }
 }
diff --git a/include/class.email.php b/include/class.email.php
index b03fbb6f07d5abb6aaa09d12cadc1fe884bd1042..d38806f75f668c558872f2035208edeab924f2b2 100644
--- a/include/class.email.php
+++ b/include/class.email.php
@@ -241,38 +241,39 @@ class Email {
 
     function save($id,$vars,&$errors) {
         global $cfg;
+
         //very basic checks
 
         $vars['name']=Format::striptags(trim($vars['name']));
         $vars['email']=trim($vars['email']);
 
         if($id && $id!=$vars['id'])
-            $errors['err']='Internal error. Get technical help.';
+            $errors['err']=__('Internal error. Get technical help.');
 
         if(!$vars['email'] || !Validator::is_email($vars['email'])) {
-            $errors['email']='Valid email required';
+            $errors['email']=__('Valid email required');
         }elseif(($eid=Email::getIdByEmail($vars['email'])) && $eid!=$id) {
-            $errors['email']='Email already exists';
+            $errors['email']=__('Email already exists');
         }elseif($cfg && !strcasecmp($cfg->getAdminEmail(), $vars['email'])) {
-            $errors['email']='Email already used as admin email!';
+            $errors['email']=__('Email already used as admin email!');
         }elseif(Staff::getIdByEmail($vars['email'])) { //make sure the email doesn't belong to any of the staff
-            $errors['email']='Email in use by a staff member';
+            $errors['email']=__('Email in use by an agent');
         }
 
         if(!$vars['name'])
-            $errors['name']='Email name required';
+            $errors['name']=__('Email name required');
 
         if($vars['mail_active'] || ($vars['smtp_active'] && $vars['smtp_auth'])) {
             if(!$vars['userid'])
-                $errors['userid']='Username missing';
+                $errors['userid']=__('Username missing');
 
             if(!$id && !$vars['passwd'])
-                $errors['passwd']='Password required';
+                $errors['passwd']=__('Password required');
             elseif($vars['passwd']
                     && $vars['userid']
                     && !Crypto::encrypt($vars['passwd'], SECRET_SALT, $vars['userid'])
                     )
-                $errors['passwd'] = 'Unable to encrypt password - get technical support';
+                $errors['passwd'] = __('Unable to encrypt password - get technical support');
         }
 
         list($vars['mail_protocol'], $encryption) = explode('/', $vars['mail_proto']);
@@ -281,29 +282,29 @@ class Email {
         if($vars['mail_active']) {
             //Check pop/imapinfo only when enabled.
             if(!function_exists('imap_open'))
-                $errors['mail_active']= 'IMAP doesn\'t exist. PHP must be compiled with IMAP enabled.';
+                $errors['mail_active']= __("IMAP doesn't exist. PHP must be compiled with IMAP enabled.");
             if(!$vars['mail_host'])
-                $errors['mail_host']='Host name required';
+                $errors['mail_host']=__('Host name required');
             if(!$vars['mail_port'])
-                $errors['mail_port']='Port required';
+                $errors['mail_port']=__('Port required');
             if(!$vars['mail_protocol'])
-                $errors['mail_protocol']='Select protocol';
+                $errors['mail_protocol']=__('Select protocol');
             if(!$vars['mail_fetchfreq'] || !is_numeric($vars['mail_fetchfreq']))
-                $errors['mail_fetchfreq']='Fetch interval required';
+                $errors['mail_fetchfreq']=__('Fetch interval required');
             if(!$vars['mail_fetchmax'] || !is_numeric($vars['mail_fetchmax']))
-                $errors['mail_fetchmax']='Maximum emails required';
+                $errors['mail_fetchmax']=__('Maximum emails required');
 
             if(!isset($vars['postfetch']))
-                $errors['postfetch']='Indicate what to do with fetched emails';
+                $errors['postfetch']=__('Indicate what to do with fetched emails');
             elseif(!strcasecmp($vars['postfetch'],'archive') && !$vars['mail_archivefolder'] )
-                $errors['postfetch']='Valid folder required';
+                $errors['postfetch']=__('Valid folder required');
         }
 
         if($vars['smtp_active']) {
             if(!$vars['smtp_host'])
-                $errors['smtp_host']='Host name required';
+                $errors['smtp_host']=__('Host name required');
             if(!$vars['smtp_port'])
-                $errors['smtp_port']='Port required';
+                $errors['smtp_port']=__('Port required');
         }
 
         //abort on errors
@@ -316,7 +317,7 @@ class Email {
                 $sql.=' AND email_id!='.db_input($id);
 
             if(db_num_rows(db_query($sql)))
-                $errors['userid']=$errors['host']='Host/userid combination already in use.';
+                $errors['userid']=$errors['host']=__('Host/userid combination already in use.');
         }
 
         $passwd=$vars['passwd']?$vars['passwd']:$vars['cpasswd'];
@@ -332,12 +333,14 @@ class Email {
                         'encryption' => $vars['mail_encryption'])
                     );
             if(!$fetcher->connect()) {
-                $errors['err']='Invalid login. Check '.Format::htmlchars($vars['mail_protocol']).' settings';
+                //$errors['err']='Invalid login. Check '.Format::htmlchars($vars['mail_protocol']).' settings';
+                $errors['err']=sprintf(__('Invalid login. Check %s settings'),Format::htmlchars($vars['mail_protocol']));
                 $errors['mail']='<br>'.$fetcher->getLastError();
             }elseif($vars['mail_archivefolder'] && !$fetcher->checkMailbox($vars['mail_archivefolder'],true)) {
-                 $errors['postfetch']='Invalid or unknown mail folder! >> '.$fetcher->getLastError().'';
+                 //$errors['postfetch']='Invalid or unknown mail folder! >> '.$fetcher->getLastError().'';
+                 $errors['postfetch']=sprintf(__('Invalid or unknown mail folder! >> %s'),$fetcher->getLastError());
                  if(!$errors['mail'])
-                     $errors['mail']='Invalid or unknown archive folder!';
+                     $errors['mail']=__('Invalid or unknown archive folder!');
             }
         }
 
@@ -354,7 +357,7 @@ class Email {
                            ));
             $mail = $smtp->connect();
             if(PEAR::isError($mail)) {
-                $errors['err']='Unable to log in. Check SMTP settings.';
+                $errors['err']=__('Unable to log in. Check SMTP settings.');
                 $errors['smtp']='<br>'.$mail->getMessage();
             }else{
                 $smtp->disconnect(); //Thank you, sir!
@@ -401,13 +404,15 @@ class Email {
             if(db_query($sql) && db_affected_rows())
                 return true;
 
-            $errors['err']='Unable to update email. Internal error occurred';
+            $errors['err']=sprintf(__('Unable to update %s.'), __('this email'))
+               .' '.__('Internal error occurred');
         }else {
             $sql='INSERT INTO '.EMAIL_TABLE.' SET '.$sql.',created=NOW()';
             if(db_query($sql) && ($id=db_insert_id()))
                 return $id;
 
-            $errors['err']='Unable to add email. Internal error';
+            $errors['err']=sprintf(__('Unable to add %s.'), __('this email'))
+               .' '.__('Internal error occurred');
         }
 
         return false;
diff --git a/include/class.error.php b/include/class.error.php
index 602304f763c5166b75b854c43597a893f6d42220..7e9ecb8a9cc15cc45d422fd7a13d39278af95091 100644
--- a/include/class.error.php
+++ b/include/class.error.php
@@ -24,7 +24,8 @@ class Error extends Exception {
     function __construct($message) {
         global $ost;
 
-        $message = str_replace(ROOT_DIR, '(root)/', $message);
+        parent::__construct(__($message));
+        $message = str_replace(ROOT_DIR, '(root)/', _S($message));
 
         if ($ost->getConfig()->getLogLevel() == 3)
             $message .= "\n\n" . $this->getBacktrace();
@@ -33,7 +34,7 @@ class Error extends Exception {
     }
 
     function getTitle() {
-        return get_class($this) . ': ' . static::$title;
+        return get_class($this) . ': ' . _S(static::$title);
     }
 
     function getBacktrace() {
diff --git a/include/class.export.php b/include/class.export.php
index c5ea494eb5ff3591127bbdac446c951b0d4d8128..521eb7cefbdb89605ebe87e04ea2d644f359d60c 100644
--- a/include/class.export.php
+++ b/include/class.export.php
@@ -16,6 +16,14 @@
 
 class Export {
 
+    // XXX: This may need to be moved to a print-specific class
+    static $paper_sizes = array(
+        /* @trans */ 'Letter',
+        /* @trans */ 'Legal',
+        'A4',
+        'A3',
+    );
+
     static function dumpQuery($sql, $headers, $how='csv', $options=array()) {
         $exporters = array(
             'csv' => CsvResultsExporter,
@@ -57,25 +65,25 @@ class Export {
             $sql = str_replace(' FROM ', ',' . implode(',', $select) . ' FROM ', $sql);
         return self::dumpQuery($sql,
             array(
-                'number' =>         'Ticket Number',
-                'ticket_created' => 'Date',
-                'subject' =>        'Subject',
-                'name' =>           'From',
-                'email' =>          'From Email',
-                'priority_desc' =>  'Priority',
-                'dept_name' =>      'Department',
-                'helptopic' =>      'Help Topic',
-                'source' =>         'Source',
-                'status' =>         'Current Status',
-                'effective_date' => 'Last Updated',
-                'duedate' =>        'Due Date',
-                'isoverdue' =>      'Overdue',
-                'isanswered' =>     'Answered',
-                'assigned' =>       'Assigned To',
-                'staff' =>          'Staff Assigned',
-                'team' =>           'Team Assigned',
-                'thread_count' =>   'Thread Count',
-                'attachments' =>    'Attachment Count',
+                'number' =>         __('Ticket Number'),
+                'ticket_created' => __('Date'),
+                'subject' =>        __('Subject'),
+                'name' =>           __('From'),
+                'email' =>          __('From Email'),
+                'priority_desc' =>  __('Priority'),
+                'dept_name' =>      __('Department'),
+                'helptopic' =>      __('Help Topic'),
+                'source' =>         __('Source'),
+                'status' =>         __('Current Status'),
+                'effective_date' => __('Last Updated'),
+                'duedate' =>        __('Due Date'),
+                'isoverdue' =>      __('Overdue'),
+                'isanswered' =>     __('Answered'),
+                'assigned' =>       __('Assigned To'),
+                'staff' =>          __('Agent Assigned'),
+                'team' =>           __('Team Assigned'),
+                'thread_count' =>   __('Thread Count'),
+                'attachments' =>    __('Attachment Count'),
             ) + $cdata,
             $how,
             array('modify' => function(&$record, $keys) use ($fields) {
@@ -127,9 +135,9 @@ class Export {
         ob_start();
         echo self::dumpQuery($sql,
                 array(
-                    'name'  =>  'Name',
-                    'organization' => 'Organization',
-                    'email' =>  'Email'
+                    'name'  =>          __('Name'),
+                    'organization' =>   __('Organization'),
+                    'email' =>          __('Email'),
                     ) + $cdata,
                 $how,
                 array('modify' => function(&$record, $keys) use ($fields) {
@@ -361,7 +369,7 @@ class DatabaseExporter {
 
             if (!$table) {
                 if ($error_stream) $error_stream->write(
-                    $t.': Cannot export table with no fields'."\n");
+                    sprintf(__("%s: Cannot export table with no fields\n"), $t));
                 die();
             }
             $this->write_block(
diff --git a/include/class.faq.php b/include/class.faq.php
index 8a006041a9c7757e7e29b781a3d38cfa680357df..0395b4968671f9a487167443d66ef242d7f9da04 100644
--- a/include/class.faq.php
+++ b/include/class.faq.php
@@ -63,6 +63,10 @@ class FAQ {
     function getAnswerWithImages() {
         return Format::viewableImages($this->ht['answer'], ROOT_PATH.'image.php');
     }
+    function getSearchableAnswer() {
+        return ThreadBody::fromFormattedText($this->ht['answer'], 'html')
+            ->getSearchable();
+    }
     function getNotes() { return $this->ht['notes']; }
     function getNumAttachments() { return $this->ht['attachments']; }
 
@@ -153,9 +157,10 @@ class FAQ {
         if($ids)
             $sql.=' AND topic_id NOT IN('.implode(',', db_input($ids)).')';
 
-        db_query($sql);
+        if (!db_query($sql))
+            return false;
 
-        return true;
+        Signal::send('model.updated', $this);
     }
 
     function update($vars, &$errors) {
@@ -166,7 +171,7 @@ class FAQ {
         $this->updateTopics($vars['topics']);
 
         //Delete removed attachments.
-        $keepers = $vars['files']?$vars['files']:array();
+        $keepers = $vars['files'];
         if(($attachments = $this->attachments->getSeparates())) {
             foreach($attachments as $file) {
                 if($file['id'] && !in_array($file['id'], $keepers))
@@ -174,9 +179,8 @@ class FAQ {
             }
         }
 
-        //Upload new attachments IF any.
-        if($_FILES['attachments'] && ($files=AttachmentFile::format($_FILES['attachments'])))
-            $this->attachments->upload($files);
+        // Upload new attachments IF any.
+        $this->attachments->upload($keepers);
 
         // Inline images (attached to the draft)
         $this->attachments->deleteInlines();
@@ -184,6 +188,7 @@ class FAQ {
 
         $this->reload();
 
+        Signal::send('model.updated', $this);
         return true;
     }
 
@@ -285,18 +290,18 @@ class FAQ {
 
         //validate
         if($id && $id!=$vars['id'])
-            $errors['err'] = 'Internal error. Try again';
+            $errors['err'] = __('Internal error. Try again');
 
         if(!$vars['question'])
-            $errors['question'] = 'Question required';
+            $errors['question'] = __('Question required');
         elseif(($qid=self::findIdByQuestion($vars['question'])) && $qid!=$id)
-            $errors['question'] = 'Question already exists';
+            $errors['question'] = __('Question already exists');
 
         if(!$vars['category_id'] || !($category=Category::lookup($vars['category_id'])))
-            $errors['category_id'] = 'Category is required';
+            $errors['category_id'] = __('Category is required');
 
         if(!$vars['answer'])
-            $errors['answer'] = 'FAQ answer is required';
+            $errors['answer'] = __('FAQ answer is required');
 
         if($errors || $validation) return (!$errors);
 
@@ -313,14 +318,17 @@ class FAQ {
             if(db_query($sql))
                 return true;
 
-            $errors['err']='Unable to update FAQ.';
+            $errors['err']=sprintf(__('Unable to update %s.'), __('this FAQ article'));
 
         } else {
             $sql='INSERT INTO '.FAQ_TABLE.' SET '.$sql.',created=NOW()';
-            if(db_query($sql) && ($id=db_insert_id()))
+            if (db_query($sql) && ($id=db_insert_id())) {
+                Signal::send('model.created', FAQ::lookup($id));
                 return $id;
+            }
 
-            $errors['err']='Unable to create FAQ. Internal error';
+            $errors['err']=sprintf(__('Unable to create %s.'), __('this FAQ article'))
+               .' '.__('Internal error occurred');
         }
 
         return false;
diff --git a/include/class.file.php b/include/class.file.php
index a2ce70bfa1a56b33641eda172b40651619e067fb..15f4ea17a0f8dc294672bfa4d695a7a12dc1ff09 100644
--- a/include/class.file.php
+++ b/include/class.file.php
@@ -107,13 +107,17 @@ class AttachmentFile {
         return $this->ht['created'];
     }
 
+    static function getDownloadForIdAndKey($id, $key) {
+        return strtolower($key . md5($id.session_id().strtolower($key)));
+    }
+
+
     /**
      * Retrieve a signature that can be sent to scp/file.php?h= in order to
      * download this file
      */
     function getDownloadHash() {
-        return strtolower($this->getKey()
-            . md5($this->getId().session_id().strtolower($this->getKey())));
+        return self::getDownloadForIdAndKey($this->getId(), $this->getKey());
     }
 
     function open() {
@@ -282,7 +286,7 @@ class AttachmentFile {
                 break;
             default:
                 // TODO: Return an error
-                $error = 'Invalid image file type';
+                $error = __('Invalid image file type');
                 return false;
         }
 
@@ -291,7 +295,7 @@ class AttachmentFile {
         if ($source_aspect_ratio >= $aspect_ratio)
             return self::upload($file, 'L');
 
-        $error = 'Image is too square. Upload a wider image';
+        $error = __('Image is too square. Upload a wider image');
         return false;
     }
 
@@ -530,9 +534,8 @@ class AttachmentFile {
 
     /*
       Method formats http based $_FILE uploads - plus basic validation.
-      @restrict - make sure file type & size are allowed.
      */
-    function format($files, $restrict=false) {
+    function format($files) {
         global $ost;
 
         if(!$files || !is_array($files))
@@ -558,16 +561,6 @@ class AttachmentFile {
                 $file['error'] = 'File upload error #'.$file['error'];
             elseif(!$file['tmp_name'] || !is_uploaded_file($file['tmp_name']))
                 $file['error'] = 'Invalid or bad upload POST';
-            elseif($restrict) { // make sure file type & size are allowed.
-                if(!$ost->isFileTypeAllowed($file))
-                    $file['error'] = 'Invalid file type for '.Format::htmlchars($file['name']);
-                elseif($ost->getConfig()->getMaxFileSize()
-                        && $file['size']>$ost->getConfig()->getMaxFileSize())
-                    $file['error'] = sprintf('File %s (%s) is too big. Maximum of %s allowed',
-                            Format::htmlchars($file['name']),
-                            Format::file_size($file['size']),
-                            Format::file_size($ost->getConfig()->getMaxFileSize()));
-            }
         }
         unset($file);
 
@@ -587,7 +580,7 @@ class AttachmentFile {
                 .'SELECT file_id FROM '.TICKET_ATTACHMENT_TABLE
                 .' UNION '
                 .'SELECT file_id FROM '.ATTACHMENT_TABLE
-            .") AND `ft` = 'T'";
+            .") AND `ft` = 'T' AND TIMESTAMPDIFF(DAY, `created`, CURRENT_TIMESTAMP) > 1";
 
         if (!($res = db_query($sql)))
             return false;
diff --git a/include/class.filter.php b/include/class.filter.php
index 2370ab6640ba791257c8e96aecc34b46874f5f61..d30a6c68b9186a9e5a7ca9b7cb56346ad286b7b2 100644
--- a/include/class.filter.php
+++ b/include/class.filter.php
@@ -13,22 +13,23 @@
 
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
+
 class Filter {
 
     var $id;
     var $ht;
 
     static $match_types = array(
-        'User Information' => array(
-            array('name'      => 'Name',
-                'email'     => 'Email',
+        /* @trans */ 'User Information' => array(
+            array('name'      =>    /* @trans */ 'Name',
+                'email'     =>      /* @trans */ 'Email',
             ),
             900
         ),
-        'Email Meta-Data' => array(
-            array('reply-to'  => 'Reply-To Email',
-                'reply-to-name' => 'Reply-To Name',
-                'addressee' => 'Addressee (To and Cc)',
+        /* @trans */ 'Email Meta-Data' => array(
+            array('reply-to'  =>    /* @trans */ 'Reply-To Email',
+                'reply-to-name' =>  /* @trans */ 'Reply-To Name',
+                'addressee' =>      /* @trans */ 'Addressee (To and Cc)',
             ),
             200
         ),
@@ -107,6 +108,10 @@ class Filter {
         return $this->ht['dept_id'];
     }
 
+    function getStatusId() {
+        return $this->ht['status_id'];
+    }
+
     function getPriorityId() {
         return $this->ht['priority_id'];
     }
@@ -302,6 +307,8 @@ class Filter {
         if ($this->getPriorityId()) $ticket['priorityId']=$this->getPriorityId();
         #       Set SLA plan (?)
         if ($this->getSLAId())      $ticket['slaId']=$this->getSLAId();
+        #       Set status
+        if ($this->getStatusId())   $ticket['statusId']=$this->getStatusId();
         #       Auto-assign to (?)
         #       XXX: Unset the other (of staffId or teamId) (?)
         if ($this->getStaffId())    $ticket['staffId']=$this->getStaffId();
@@ -348,14 +355,14 @@ class Filter {
 
     /* static */ function getSupportedMatchTypes() {
         return array(
-            'equal'=>       'Equal',
-            'not_equal'=>   'Not Equal',
-            'contains'=>    'Contains',
-            'dn_contain'=>  'Does Not Contain',
-            'starts'=>      'Starts With',
-            'ends'=>        'Ends With',
-            'match'=>       'Matches Regex',
-            'not_match'=>   'Does Not Match Regex',
+            'equal'=>       __('Equal'),
+            'not_equal'=>   __('Not Equal'),
+            'contains'=>    __('Contains'),
+            'dn_contain'=>  __('Does Not Contain'),
+            'starts'=>      __('Starts With'),
+            'ends'=>        __('Ends With'),
+            'match'=>       __('Matches Regex'),
+            'not_match'=>   __('Does Not Match Regex'),
         );
     }
 
@@ -383,10 +390,10 @@ class Filter {
     /** static functions **/
     function getTargets() {
         return array(
-                'Any' => 'Any',
-                'Web' => 'Web Forms',
-                'API' => 'API Calls',
-                'Email' => 'Emails');
+                'Any' => __('Any'),
+                'Web' => __('Web Forms'),
+                'API' => __('API Calls'),
+                'Email' => __('Emails'));
     }
 
     function create($vars,&$errors) {
@@ -427,18 +434,18 @@ class Filter {
                 }
 
                 if(!$vars["rule_w$i"] || !in_array($vars["rule_w$i"],$matches))
-                    $errors["rule_$i"]='Invalid match selection';
+                    $errors["rule_$i"]=__('Invalid match selection');
                 elseif(!$vars["rule_h$i"] || !in_array($vars["rule_h$i"],$types))
-                    $errors["rule_$i"]='Invalid match type selection';
+                    $errors["rule_$i"]=__('Invalid match type selection');
                 elseif(!$vars["rule_v$i"])
-                    $errors["rule_$i"]='Value required';
+                    $errors["rule_$i"]=__('Value required');
                 elseif($vars["rule_w$i"]=='email'
                         && $vars["rule_h$i"]=='equal'
                         && !Validator::is_email($vars["rule_v$i"]))
-                    $errors["rule_$i"]='Valid email required for the match type';
+                    $errors["rule_$i"]=__('Valid email required for the match type');
                 elseif (in_array($vars["rule_h$i"], array('match','not_match'))
                         && (false === @preg_match($vars["rule_v$i"], ' ')))
-                    $errors["rule_$i"] = sprintf('Regex compile error: (#%s)',
+                    $errors["rule_$i"] = sprintf(__('Regex compile error: (#%s)'),
                         preg_last_error());
 
 
@@ -446,7 +453,7 @@ class Filter {
                     $rules[]=array('what'=>$vars["rule_w$i"],
                         'how'=>$vars["rule_h$i"],'val'=>$vars["rule_v$i"]);
             }elseif($vars["rule_v$i"]) {
-                $errors["rule_$i"]='Incomplete selection';
+                $errors["rule_$i"]=__('Incomplete selection');
             }
         }
 
@@ -454,7 +461,7 @@ class Filter {
             # XXX: Validation bypass
             $rules = $vars["rules"];
         elseif(!$rules && !$errors)
-            $errors['rules']='You must set at least one rule.';
+            $errors['rules']=__('You must set at least one rule.');
 
         if($errors) return false;
 
@@ -474,25 +481,24 @@ class Filter {
 
     function save($id,$vars,&$errors) {
 
-
         if(!$vars['execorder'])
-            $errors['execorder'] = 'Order required';
+            $errors['execorder'] = __('Order required');
         elseif(!is_numeric($vars['execorder']))
-            $errors['execorder'] = 'Must be numeric value';
+            $errors['execorder'] = __('Must be numeric value');
 
         if(!$vars['name'])
-            $errors['name'] = 'Name required';
+            $errors['name'] = __('Name required');
         elseif(($sid=self::getIdByName($vars['name'])) && $sid!=$id)
-            $errors['name'] = 'Name already in use';
+            $errors['name'] = __('Name already in use');
 
         if(!$errors && !self::validate_rules($vars,$errors) && !$errors['rules'])
-            $errors['rules'] = 'Unable to validate rules as entered';
+            $errors['rules'] = __('Unable to validate rules as entered');
 
         $targets = self::getTargets();
         if(!$vars['target'])
-            $errors['target'] = 'Target required';
+            $errors['target'] = __('Target required');
         else if(!is_numeric($vars['target']) && !$targets[$vars['target']])
-            $errors['target'] = 'Unknown or invalid target';
+            $errors['target'] = __('Unknown or invalid target');
 
         if($errors) return false;
 
@@ -509,6 +515,7 @@ class Filter {
             .',execorder='.db_input($vars['execorder'])
             .',email_id='.db_input($emailId)
             .',dept_id='.db_input($vars['dept_id'])
+            .',status_id='.db_input($vars['status_id'])
             .',priority_id='.db_input($vars['priority_id'])
             .',sla_id='.db_input($vars['sla_id'])
             .',topic_id='.db_input($vars['topic_id'])
@@ -532,11 +539,13 @@ class Filter {
         if($id) {
             $sql='UPDATE '.FILTER_TABLE.' SET '.$sql.' WHERE id='.db_input($id);
             if(!db_query($sql))
-                $errors['err']='Unable to update the filter. Internal error occurred';
+                $errors['err']=sprintf(__('Unable to update %s.'), __('this ticket filter'))
+                   .' '.__('Internal error occurred');
         }else{
             $sql='INSERT INTO '.FILTER_TABLE.' SET '.$sql.',created=NOW() ';
             if(!db_query($sql) || !($id=db_insert_id()))
-                $errors['err']='Unable to add filter. Internal error';
+                $errors['err']=sprintf(__('Unable to add %s.'), __('this ticket filter'))
+                   .' '.__('Internal error occurred');
         }
 
         if($errors || !$id) return false;
@@ -634,9 +643,8 @@ class FilterRule {
     }
 
     /* static private */ function save($id,$vars,&$errors) {
-
         if(!$vars['filter_id'])
-            $errors['err']='Parent filter ID required';
+            $errors['err']=__('Parent filter ID required');
 
 
         if($errors) return false;
diff --git a/include/class.format.php b/include/class.format.php
index 8d302aaa858e1e6032ab15bfe0c461669e17744b..55635f5aab7d6cd57dc6ad9b90c3cc9ff37b6ce3 100644
--- a/include/class.format.php
+++ b/include/class.format.php
@@ -30,7 +30,7 @@ class Format {
         return round(($bytes/1048576),1).' mb';
     }
 
-    /* encode text into desired encoding - taking into accout charset when available. */
+	/* encode text into desired encoding - taking into accout charset when available. */
     function encode($text, $charset=null, $encoding='utf-8') {
 
         //Try auto-detecting charset/encoding
@@ -270,12 +270,16 @@ class Format {
     }
 
     function htmlencode($var) {
+        static $phpversion = null;
 
         if (is_array($var))
             return array_map(array('Format', 'htmlencode'), $var);
 
+        if (!isset($phpversion))
+            $phpversion = phpversion();
+
         $flags = ENT_COMPAT;
-        if (phpversion() >= '5.4.0')
+        if ($phpversion >= '5.4.0')
             $flags |= ENT_HTML401;
 
         try {
@@ -570,5 +574,65 @@ class Format {
         );
     }
 
+    // Performs Unicode normalization (where possible) and splits words at
+    // difficult word boundaries (for far eastern languages)
+    function searchable($text, $lang=false) {
+        global $cfg;
+
+        if (function_exists('normalizer_normalize')) {
+            // Normalize text input :: remove diacritics and such
+            $text = normalizer_normalize($text, Normalizer::FORM_C);
+        }
+        else {
+            // As a lightweight compatiblity, use a lightweight C
+            // normalizer with diacritic removal, thanks
+            // http://ahinea.com/en/tech/accented-translate.html
+            $tr = array(
+                "ä" => "a", "ñ" => "n", "ö" => "o", "ü" => "u", "ÿ" => "y"
+            );
+            $text = strtr($text, $tr);
+        }
+        // Decompose compatible versions of characters (ä => ae)
+        $tr = array(
+            "ß" => "ss", "Æ" => "AE", "æ" => "ae", "IJ" => "IJ",
+            "ij" => "ij", "Œ" => "OE", "œ" => "oe", "Ð" => "D",
+            "Đ" => "D", "ð" => "d", "đ" => "d", "Ħ" => "H", "ħ" => "h",
+            "ı" => "i", "ĸ" => "k", "Ŀ" => "L", "Ł" => "L", "ŀ" => "l",
+            "ł" => "l", "Ŋ" => "N", "ʼn" => "n", "ŋ" => "n", "Ø" => "O",
+            "ø" => "o", "ſ" => "s", "Þ" => "T", "Ŧ" => "T", "þ" => "t",
+            "ŧ" => "t", "ä" => "ae", "ö" => "oe", "ü" => "ue",
+            "Ä" => "AE", "Ö" => "OE", "Ü" => "UE",
+        );
+        $text = strtr($text, $tr);
+
+        // Drop separated diacritics
+        $text = preg_replace('/\p{M}/u', '', $text);
+
+        // Drop extraneous whitespace
+        $text = preg_replace('/(\s)\s+/u', '$1', $text);
+
+        // Drop leading and trailing whitespace
+        $text = trim($text);
+
+        if (class_exists('IntlBreakIterator')) {
+            // Split by word boundaries
+            if ($tokenizer = IntlBreakIterator::createWordInstance(
+                    $lang ?: ($cfg ? $cfg->getSystemLanguage() : 'en_US'))
+            ) {
+                $tokenizer->setText($text);
+                $tokens = array();
+                foreach ($tokenizer as $token)
+                    $tokens[] = $token;
+                $text = implode(' ', $tokens);
+            }
+        }
+        else {
+            // Approximate word boundaries from Unicode chart at
+            // http://www.unicode.org/reports/tr29/#Word_Boundaries
+
+            // Punt for now
+        }
+        return $text;
+    }
 }
 ?>
diff --git a/include/class.forms.php b/include/class.forms.php
index dbdfd37908fa23de31c793a4c766d4da850cbd41..54ef6e9003821df2da2091adcf0b0125b01f61e1 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -20,7 +20,7 @@
  */
 class Form {
     var $fields = array();
-    var $title = 'Unnamed';
+    var $title = '';
     var $instructions = '';
 
     var $_errors = null;
@@ -28,8 +28,11 @@ class Form {
 
     function __construct($fields=array(), $source=null, $options=array()) {
         $this->fields = $fields;
-        foreach ($fields as $f)
+        foreach ($fields as $k=>$f) {
             $f->setForm($this);
+            if (!$f->get('name') && $k)
+                $f->set('name', $k);
+        }
         if (isset($options['title']))
             $this->title = $options['title'];
         if (isset($options['instructions']))
@@ -59,6 +62,7 @@ class Form {
     function getTitle() { return $this->title; }
     function getInstructions() { return $this->instructions; }
     function getSource() { return $this->_source; }
+    function setSource($source) { $this->_source = $source; }
 
     /**
      * Validate the form and indicate if there no errors.
@@ -82,7 +86,7 @@ class Form {
         if (!$this->_clean) {
             $this->_clean = array();
             foreach ($this->getFields() as $key=>$field) {
-                if (!$field->hasData())
+                if ($field->isPresentationOnly())
                     continue;
                 $this->_clean[$key] = $this->_clean[$field->get('name')]
                     = $field->getClean();
@@ -107,6 +111,40 @@ class Form {
         else
             include(CLIENTINC_DIR . 'templates/dynamic-form.tmpl.php');
     }
+
+    function getMedia() {
+        static $dedup = array();
+
+        foreach ($this->getFields() as $f) {
+            if (($M = $f->getMedia()) && is_array($M)) {
+                foreach ($M as $type=>$files) {
+                    foreach ($files as $url) {
+                        $key = strtolower($type.$url);
+                        if (isset($dedup[$key]))
+                            continue;
+
+                        self::emitMedia($url, $type);
+
+                        $dedup[$key] = true;
+                    }
+                }
+            }
+        }
+    }
+
+    static function emitMedia($url, $type) {
+        if ($url[0] == '/')
+            $url = ROOT_PATH . substr($url, 1);
+
+        switch (strtolower($type)) {
+        case 'css': ?>
+        <link rel="stylesheet" type="text/css" href="<?php echo $url; ?>"/><?php
+            break;
+        case 'js': ?>
+        <script type="text/javascript" src="<?php echo $url; ?>"></script><?php
+            break;
+        }
+    }
 }
 
 require_once(INCLUDE_DIR . "class.json.php");
@@ -131,15 +169,17 @@ class FormField {
     var $presentation_only = false;
 
     static $types = array(
-        'Basic Fields' => array(
-            'text'  => array('Short Answer', 'TextboxField'),
-            'memo' => array('Long Answer', 'TextareaField'),
-            'thread' => array('Thread Entry', 'ThreadEntryField', false),
-            'datetime' => array('Date and Time', 'DatetimeField'),
-            'phone' => array('Phone Number', 'PhoneField'),
-            'bool' => array('Checkbox', 'BooleanField'),
-            'choices' => array('Choices', 'ChoiceField'),
-            'break' => array('Section Break', 'SectionBreakField'),
+        /* @trans */ 'Basic Fields' => array(
+            'text'  => array(   /* @trans */ 'Short Answer', 'TextboxField'),
+            'memo' => array(    /* @trans */ 'Long Answer', 'TextareaField'),
+            'thread' => array(  /* @trans */ 'Thread Entry', 'ThreadEntryField', false),
+            'datetime' => array(/* @trans */ 'Date and Time', 'DatetimeField'),
+            'phone' => array(   /* @trans */ 'Phone Number', 'PhoneField'),
+            'bool' => array(    /* @trans */ 'Checkbox', 'BooleanField'),
+            'choices' => array( /* @trans */ 'Choices', 'ChoiceField'),
+            'files' => array(   /* @trans */ 'File Upload', 'FileUploadField'),
+            'break' => array(   /* @trans */ 'Section Break', 'SectionBreakField'),
+            'info' => array(    /* @trans */ 'Information', 'FreeTextField'),
         ),
     );
     static $more_types = array();
@@ -157,13 +197,16 @@ class FormField {
     }
 
     static function addFieldTypes($group, $callable) {
-        static::$more_types[$group] = $callable;
+        static::$more_types[$group][] = $callable;
     }
 
     static function allTypes() {
         if (static::$more_types) {
-            foreach (static::$more_types as $group=>$c)
-                static::$types[$group] = call_user_func($c);
+            foreach (static::$more_types as $group => $entries)
+                foreach ($entries as $c)
+                    static::$types[$group] = array_merge(
+                            static::$types[$group] ?: array(), call_user_func($c));
+
             static::$more_types = array();
         }
         return static::$types;
@@ -178,6 +221,9 @@ class FormField {
     function get($what) {
         return $this->ht[$what];
     }
+    function set($field, $value) {
+        $this->ht[$field] = $value;
+    }
 
     /**
      * getClean
@@ -191,7 +237,21 @@ class FormField {
         if (!isset($this->_clean)) {
             $this->_clean = (isset($this->value))
                 ? $this->value : $this->parse($this->getWidget()->value);
-            $this->validateEntry($this->_clean);
+
+            if ($vs = $this->get('cleaners')) {
+                if (is_array($vs)) {
+                    foreach ($vs as $cleaner)
+                        if (is_callable($cleaner))
+                            $this->_clean = call_user_func_array(
+                                    $cleaner, array($this, $this->_clean));
+                }
+                elseif (is_callable($vs))
+                    $this->_clean = call_user_func_array(
+                            $vs, array($this, $this->_clean));
+            }
+
+            if ($this->isVisible())
+                $this->validateEntry($this->_clean);
         }
         return $this->_clean;
     }
@@ -231,7 +291,8 @@ class FormField {
         # Validates a user-input into an instance of this field on a dynamic
         # form
         if ($this->get('required') && !$value && $this->hasData())
-            $this->_errors[] = sprintf('%s is a required field', $this->getLabel());
+            $this->_errors[] = sprintf(__('%s is a required field'),
+                $this->getLabel());
 
         # Perform declared validators for the field
         if ($vs = $this->get('validators')) {
@@ -245,6 +306,31 @@ class FormField {
         }
     }
 
+    /**
+     * isVisible
+     *
+     * If this field has visibility configuration, then it will parse the
+     * constraints with the visibility configuration to determine if the
+     * field is visible and should be considered for validation
+     */
+    function isVisible() {
+        $config = $this->getConfiguration();
+        if ($this->get('visibility') instanceof VisibilityConstraint) {
+            return $this->get('visibility')->isVisible($this);
+        }
+        return true;
+    }
+
+    /**
+     * FIXME: Temp
+     *
+     */
+
+    function isEditable() {
+        return (($this->get('edit_mask') & 32) == 0);
+    }
+
+
     /**
      * parse
      *
@@ -256,7 +342,7 @@ class FormField {
      * useful error message indicating what is wrong with the input.
      */
     function parse($value) {
-        return trim($value);
+        return is_string($value) ? trim($value) : $value;
     }
 
     /**
@@ -306,6 +392,10 @@ class FormField {
         return (string) $value;
     }
 
+    function __toString() {
+        return $this->toString($this->value);
+    }
+
     /**
      * Returns an HTML friendly value for the data in the field.
      */
@@ -330,6 +420,10 @@ class FormField {
         return $this->toString($this->getClean());
     }
 
+    function searchable($value) {
+        return Format::searchable($this->toString($value));
+    }
+
     function getLabel() { return $this->get('label'); }
 
     /**
@@ -357,7 +451,8 @@ class FormField {
     function __call($what, $args) {
         // XXX: Throw exception if $this->parent is not set
         if (!$this->parent)
-            throw new Exception($what.': Call to undefined function');
+            throw new Exception(sprintf(__('%s: Call to undefined function'),
+                $what));
         // BEWARE: DynamicFormField has a __call() which will create a new
         //      FormField instance and invoke __call() on it or bounce
         //      immediately back
@@ -373,7 +468,7 @@ class FormField {
             return substr(md5(
                 session_id() . '-field-id-'.$this->get('id')), -16);
         else
-            return $this->get('id');
+            return $this->get('name') ?: $this->get('id');
     }
 
     function setForm($form) {
@@ -397,13 +492,22 @@ class FormField {
     }
 
     function render($mode=null) {
-        $this->getWidget()->render($mode);
+        $rv = $this->getWidget()->render($mode);
+        if ($v = $this->get('visibility')) {
+            $v->emitJavascript($this);
+        }
+        return $rv;
     }
 
     function renderExtras($mode=null) {
         return;
     }
 
+    function getMedia() {
+        $widget = $this->getWidget();
+        return $widget::$media;
+    }
+
     function getConfigurationOptions() {
         return array();
     }
@@ -478,12 +582,44 @@ class FormField {
         return false;
     }
 
-    function getConfigurationForm() {
+    /**
+     * Indicates if the field has subfields accessible via getSubFields()
+     * method. Useful for filter integration. Should connect with
+     * getFilterData()
+     */
+    function hasSubFields() {
+        return false;
+    }
+    function getSubFields() {
+        return null;
+    }
+
+    /**
+     * Indicates if the field provides for searching for something other
+     * than keywords. For instance, textbox fields can have hits by keyword
+     * searches alone, but selection fields should provide the option to
+     * match a specific value or set of values and therefore need to
+     * participate on any search builder.
+     */
+    function hasSpecialSearch() {
+        return true;
+    }
+
+    function getConfigurationForm($source=null) {
         if (!$this->_cform) {
             $type = static::getFieldType($this->get('type'));
             $clazz = $type[1];
-            $T = new $clazz();
-            $this->_cform = $T->getConfigurationOptions();
+            $T = new $clazz(array('type'=>$this->get('type')));
+            $config = $this->getConfiguration();
+            $this->_cform = new Form($T->getConfigurationOptions(), $source);
+            if (!$source) {
+                foreach ($this->_cform->getFields() as $name=>$f) {
+                    if ($config && isset($config[$name]))
+                        $f->value = $config[$name];
+                    elseif ($f->get('default'))
+                        $f->value = $f->get('default');
+                }
+            }
         }
         return $this->_cform;
     }
@@ -493,11 +629,11 @@ class FormField {
         $this->_config[$prop] = $value;
     }
 
-    function getWidget() {
+    function getWidget($widgetClass=false) {
         if (!static::$widget)
-            throw new Exception('Widget not defined for this field');
+            throw new Exception(__('Widget not defined for this field'));
         if (!isset($this->_widget)) {
-            $wc = $this->get('widget') ? $this->get('widget') : static::$widget;
+            $wc = $widgetClass ?: $this->get('widget') ?: static::$widget;
             $this->_widget = new $wc($this);
             $this->_widget->parseValue();
         }
@@ -519,39 +655,72 @@ class TextboxField extends FormField {
     function getConfigurationOptions() {
         return array(
             'size'  =>  new TextboxField(array(
-                'id'=>1, 'label'=>'Size', 'required'=>false, 'default'=>16,
+                'id'=>1, 'label'=>__('Size'), 'required'=>false, 'default'=>16,
                     'validator' => 'number')),
             'length' => new TextboxField(array(
-                'id'=>2, 'label'=>'Max Length', 'required'=>false, 'default'=>30,
+                'id'=>2, 'label'=>__('Max Length'), 'required'=>false, 'default'=>30,
                     'validator' => 'number')),
             'validator' => new ChoiceField(array(
-                'id'=>3, 'label'=>'Validator', 'required'=>false, 'default'=>'',
-                'choices' => array('phone'=>'Phone Number','email'=>'Email Address',
-                    'ip'=>'IP Address', 'number'=>'Number', ''=>'None'))),
+                'id'=>3, 'label'=>__('Validator'), 'required'=>false, 'default'=>'',
+                'choices' => array('phone'=>__('Phone Number'),'email'=>__('Email Address'),
+                    'ip'=>__('IP Address'), 'number'=>__('Number'),
+                    'regex'=>__('Custom (Regular Expression)'), ''=>__('None')))),
+            'regex' => new TextboxField(array(
+                'id'=>6, 'label'=>__('Regular Expression'), 'required'=>true,
+                'configuration'=>array('size'=>40, 'length'=>100),
+                'visibility' => new VisibilityConstraint(
+                    new Q(array('validator__eq'=>'regex')),
+                    VisibilityConstraint::HIDDEN
+                ),
+                'cleaners' => function ($self, $value) {
+                    $wrapped = "/".$value."/iu";
+                    if (false === @preg_match($value, ' ')
+                            && false !== @preg_match($wrapped, ' ')) {
+                        return $wrapped;
+                    }
+                    if ($value == '//iu')
+                        return '';
+
+                    return $value;
+                },
+                'validators' => function($self, $v) {
+                    if (false === @preg_match($v, ' '))
+                        $self->addError(__('Cannot compile this regular expression'));
+                })),
             'validator-error' => new TextboxField(array(
-                'id'=>4, 'label'=>'Validation Error', 'default'=>'',
+                'id'=>4, 'label'=>__('Validation Error'), 'default'=>'',
                 'configuration'=>array('size'=>40, 'length'=>60),
-                'hint'=>'Message shown to user if the input does not match the validator')),
+                'hint'=>__('Message shown to user if the input does not match the validator'))),
             'placeholder' => new TextboxField(array(
-                'id'=>5, 'label'=>'Placeholder', 'required'=>false, 'default'=>'',
-                'hint'=>'Text shown in before any input from the user',
+                'id'=>5, 'label'=>__('Placeholder'), 'required'=>false, 'default'=>'',
+                'hint'=>__('Text shown in before any input from the user'),
                 'configuration'=>array('size'=>40, 'length'=>40),
             )),
         );
     }
 
+    function hasSpecialSearch() {
+        return false;
+    }
+
     function validateEntry($value) {
         parent::validateEntry($value);
         $config = $this->getConfiguration();
         $validators = array(
             '' =>       null,
             'email' =>  array(array('Validator', 'is_email'),
-                'Enter a valid email address'),
+                __('Enter a valid email address')),
             'phone' =>  array(array('Validator', 'is_phone'),
-                'Enter a valid phone number'),
+                __('Enter a valid phone number')),
             'ip' =>     array(array('Validator', 'is_ip'),
-                'Enter a valid IP address'),
-            'number' => array('is_numeric', 'Enter a number')
+                __('Enter a valid IP address')),
+            'number' => array('is_numeric', __('Enter a number')),
+            'regex' => array(
+                function($v) use ($config) {
+                    $regex = $config['regex'];
+                    return @preg_match($regex, $v);
+                }, __('Value does not match required pattern')
+            ),
         );
         // Support configuration forms, as well as GUI-based form fields
         $valid = $this->get('validator');
@@ -588,22 +757,26 @@ class TextareaField extends FormField {
     function getConfigurationOptions() {
         return array(
             'cols'  =>  new TextboxField(array(
-                'id'=>1, 'label'=>'Width (chars)', 'required'=>true, 'default'=>40)),
+                'id'=>1, 'label'=>__('Width').' '.__('(chars)'), 'required'=>true, 'default'=>40)),
             'rows'  =>  new TextboxField(array(
-                'id'=>2, 'label'=>'Height (rows)', 'required'=>false, 'default'=>4)),
+                'id'=>2, 'label'=>__('Height').' '.__('(rows)'), 'required'=>false, 'default'=>4)),
             'length' => new TextboxField(array(
-                'id'=>3, 'label'=>'Max Length', 'required'=>false, 'default'=>0)),
+                'id'=>3, 'label'=>__('Max Length'), 'required'=>false, 'default'=>0)),
             'html' => new BooleanField(array(
-                'id'=>4, 'label'=>'HTML', 'required'=>false, 'default'=>true,
-                'configuration'=>array('desc'=>'Allow HTML input in this box'))),
+                'id'=>4, 'label'=>__('HTML'), 'required'=>false, 'default'=>true,
+                'configuration'=>array('desc'=>__('Allow HTML input in this box')))),
             'placeholder' => new TextboxField(array(
-                'id'=>5, 'label'=>'Placeholder', 'required'=>false, 'default'=>'',
-                'hint'=>'Text shown in before any input from the user',
+                'id'=>5, 'label'=>__('Placeholder'), 'required'=>false, 'default'=>'',
+                'hint'=>__('Text shown in before any input from the user'),
                 'configuration'=>array('size'=>40, 'length'=>40),
             )),
         );
     }
 
+    function hasSpecialSearch() {
+        return false;
+    }
+
     function display($value) {
         $config = $this->getConfiguration();
         if ($config['html'])
@@ -612,6 +785,12 @@ class TextareaField extends FormField {
             return nl2br(Format::htmlchars($value));
     }
 
+    function searchable($value) {
+        $value = preg_replace(array('`<br(\s*)?/?>`i', '`</div>`i'), "\n", $value);
+        $value = Format::htmldecode(Format::striptags($value));
+        return Format::searchable($value);
+    }
+
     function export($value) {
         return (!$value) ? $value : Format::html2text($value);
     }
@@ -632,24 +811,28 @@ class PhoneField extends FormField {
     function getConfigurationOptions() {
         return array(
             'ext' => new BooleanField(array(
-                'label'=>'Extension', 'default'=>true,
+                'label'=>__('Extension'), 'default'=>true,
                 'configuration'=>array(
-                    'desc'=>'Add a separate field for the extension',
+                    'desc'=>__('Add a separate field for the extension'),
                 ),
             )),
             'digits' => new TextboxField(array(
-                'label'=>'Minimum length', 'default'=>7,
-                'hint'=>'Fewest digits allowed in a valid phone number',
+                'label'=>__('Minimum length'), 'default'=>7,
+                'hint'=>__('Fewest digits allowed in a valid phone number'),
                 'configuration'=>array('validator'=>'number', 'size'=>5),
             )),
             'format' => new ChoiceField(array(
-                'label'=>'Display format', 'default'=>'us',
-                'choices'=>array(''=>'-- Unformatted --',
-                    'us'=>'United States'),
+                'label'=>__('Display format'), 'default'=>'us',
+                'choices'=>array(''=>'-- '.__('Unformatted').' --',
+                    'us'=>__('United States')),
             )),
         );
     }
 
+    function hasSpecialSearch() {
+        return false;
+    }
+
     function validateEntry($value) {
         parent::validateEntry($value);
         $config = $this->getConfiguration();
@@ -658,12 +841,12 @@ class PhoneField extends FormField {
         if ($phone && (
                 !is_numeric($phone) ||
                 strlen($phone) < $config['digits']))
-            $this->_errors[] = "Enter a valid phone number";
+            $this->_errors[] = __("Enter a valid phone number");
         if ($ext && $config['ext']) {
             if (!is_numeric($ext))
-                $this->_errors[] = "Enter a valid phone extension";
+                $this->_errors[] = __("Enter a valid phone extension");
             elseif (!$phone)
-                $this->_errors[] = "Enter a phone number for the extension";
+                $this->_errors[] = __("Enter a phone number for the extension");
         }
     }
 
@@ -695,8 +878,8 @@ class BooleanField extends FormField {
     function getConfigurationOptions() {
         return array(
             'desc' => new TextareaField(array(
-                'id'=>1, 'label'=>'Description', 'required'=>false, 'default'=>'',
-                'hint'=>'Text shown inline with the widget',
+                'id'=>1, 'label'=>__('Description'), 'required'=>false, 'default'=>'',
+                'hint'=>__('Text shown inline with the widget'),
                 'configuration'=>array('rows'=>2)))
         );
     }
@@ -713,7 +896,7 @@ class BooleanField extends FormField {
     }
 
     function toString($value) {
-        return ($value) ? 'Yes' : 'No';
+        return ($value) ? __('Yes') : __('No');
     }
 }
 
@@ -723,43 +906,79 @@ class ChoiceField extends FormField {
     function getConfigurationOptions() {
         return array(
             'choices'  =>  new TextareaField(array(
-                'id'=>1, 'label'=>'Choices', 'required'=>false, 'default'=>'',
-                'hint'=>'List choices, one per line. To protect against
-                spelling changes, specify key:value names to preserve
-                entries if the list item names change',
+                'id'=>1, 'label'=>__('Choices'), 'required'=>false, 'default'=>'',
+                'hint'=>__('List choices, one per line. To protect against spelling changes, specify key:value names to preserve entries if the list item names change'),
                 'configuration'=>array('html'=>false)
             )),
             'default' => new TextboxField(array(
-                'id'=>3, 'label'=>'Default', 'required'=>false, 'default'=>'',
-                'hint'=>'(Enter a key). Value selected from the list initially',
+                'id'=>3, 'label'=>__('Default'), 'required'=>false, 'default'=>'',
+                'hint'=>__('(Enter a key). Value selected from the list initially'),
                 'configuration'=>array('size'=>20, 'length'=>40),
             )),
             'prompt' => new TextboxField(array(
-                'id'=>2, 'label'=>'Prompt', 'required'=>false, 'default'=>'',
-                'hint'=>'Leading text shown before a value is selected',
+                'id'=>2, 'label'=>__('Prompt'), 'required'=>false, 'default'=>'',
+                'hint'=>__('Leading text shown before a value is selected'),
                 'configuration'=>array('size'=>40, 'length'=>40),
             )),
+            'multiselect' => new BooleanField(array(
+                'id'=>1, 'label'=>'Multiselect', 'required'=>false, 'default'=>false,
+                'configuration'=>array(
+                    'desc'=>'Allow multiple selections')
+            )),
         );
     }
 
     function parse($value) {
-        if (is_numeric($value))
-            return $value;
-        foreach ($this->getChoices() as $k=>$v)
-            if (strcasecmp($value, $k) === 0)
-                return $k;
+        return $this->to_php($value ?: null);
+    }
+
+    function to_database($value) {
+        if (!is_array($value)) {
+            $choices = $this->getChoices();
+            if (isset($choices[$value]))
+                $value = array($value => $choices[$value]);
+        }
+        if (is_array($value))
+            $value = JsonDataEncoder::encode($value);
+
+        return $value;
+    }
+
+    function to_php($value) {
+        if (is_string($value))
+            $array = JsonDataParser::parse($value) ?: $value;
+        else
+            $array = $value;
+        $config = $this->getConfiguration();
+        if (is_array($array) && !$config['multiselect'] && count($array) < 2) {
+            reset($array);
+            return key($array);
+        }
+        return $array;
     }
 
     function toString($value) {
+        $selection = $this->getChoice($value);
+        return is_array($selection) ? implode(', ', array_filter($selection))
+            : (string) $selection;
+    }
+
+    function getChoice($value) {
+
         $choices = $this->getChoices();
-        if (isset($choices[$value]))
-            return $choices[$value];
-        else
-            return $choices[$this->get('default')];
+        $selection = array();
+        if ($value && is_array($value)) {
+            $selection = $value;
+        } elseif (isset($choices[$value]))
+            $selection[] = $choices[$value];
+        elseif ($this->get('default'))
+            $selection[] = $choices[$this->get('default')];
+
+        return $selection;
     }
 
-    function getChoices() {
-        if ($this->_choices === null) {
+    function getChoices($verbose=false) {
+        if ($this->_choices === null || $verbose) {
             // Allow choices to be set in this->ht (for configurationOptions)
             $this->_choices = $this->get('choices');
             if (!$this->_choices) {
@@ -773,6 +992,18 @@ class ChoiceField extends FormField {
                         $val = $key;
                     $this->_choices[trim($key)] = trim($val);
                 }
+                // Add old selections if nolonger available
+                // This is necessary so choices made previously can be
+                // retained
+                $values = ($a=$this->getAnswer()) ? $a->getValue() : array();
+                if ($values && is_array($values)) {
+                    foreach ($values as $k => $v) {
+                        if (!isset($this->_choices[$k])) {
+                            if ($verbose) $v .= ' (retired)';
+                            $this->_choices[$k] = $v;
+                        }
+                    }
+                }
             }
         }
         return $this->_choices;
@@ -825,23 +1056,24 @@ class DatetimeField extends FormField {
     function getConfigurationOptions() {
         return array(
             'time' => new BooleanField(array(
-                'id'=>1, 'label'=>'Time', 'required'=>false, 'default'=>false,
+                'id'=>1, 'label'=>__('Time'), 'required'=>false, 'default'=>false,
                 'configuration'=>array(
-                    'desc'=>'Show time selection with date picker'))),
+                    'desc'=>__('Show time selection with date picker')))),
             'gmt' => new BooleanField(array(
-                'id'=>2, 'label'=>'Timezone Aware', 'required'=>false,
+                'id'=>2, 'label'=>__('Timezone Aware'), 'required'=>false,
                 'configuration'=>array(
-                    'desc'=>"Show date/time relative to user's timezone"))),
+                    'desc'=>__("Show date/time relative to user's timezone")))),
             'min' => new DatetimeField(array(
-                'id'=>3, 'label'=>'Earliest', 'required'=>false,
-                'hint'=>'Earliest date selectable')),
+                'id'=>3, 'label'=>__('Earliest'), 'required'=>false,
+                'hint'=>__('Earliest date selectable'))),
             'max' => new DatetimeField(array(
-                'id'=>4, 'label'=>'Latest', 'required'=>false,
-                'default'=>null)),
+                'id'=>4, 'label'=>__('Latest'), 'required'=>false,
+                'default'=>null, 'hint'=>__('Latest date selectable'))),
             'future' => new BooleanField(array(
-                'id'=>5, 'label'=>'Allow Future Dates', 'required'=>false,
+                'id'=>5, 'label'=>__('Allow Future Dates'), 'required'=>false,
                 'default'=>true, 'configuration'=>array(
-                    'desc'=>'Allow entries into the future'))),
+                    'desc'=>__('Allow entries into the future' /* Used in the date field */)),
+            )),
         );
     }
 
@@ -850,12 +1082,12 @@ class DatetimeField extends FormField {
         parent::validateEntry($value);
         if (!$value) return;
         if ($config['min'] and $value < $config['min'])
-            $this->_errors[] = 'Selected date is earlier than permitted';
+            $this->_errors[] = __('Selected date is earlier than permitted');
         elseif ($config['max'] and $value > $config['max'])
-            $this->_errors[] = 'Selected date is later than permitted';
+            $this->_errors[] = __('Selected date is later than permitted');
         // strtotime returns -1 on error for PHP < 5.1.0 and false thereafter
         elseif ($value === -1 or $value === false)
-            $this->_errors[] = 'Enter a valid date';
+            $this->_errors[] = __('Enter a valid date');
     }
 }
 
@@ -887,10 +1119,35 @@ class ThreadEntryField extends FormField {
     function isPresentationOnly() {
         return true;
     }
-    function renderExtras($mode=null) {
-        if ($mode == 'client')
-            // TODO: Pass errors arrar into showAttachments
-            $this->getWidget()->showAttachments();
+    function hasSpecialSearch() {
+        return false;
+    }
+
+    function getConfigurationOptions() {
+        global $cfg;
+
+        $attachments = new FileUploadField();
+        $fileupload_config = $attachments->getConfigurationOptions();
+        $fileupload_config['extensions']->set('default', $cfg->getAllowedFileTypes());
+        return array(
+            'attachments' => new BooleanField(array(
+                'label'=>__('Enable Attachments'),
+                'default'=>$cfg->allowAttachments(),
+                'configuration'=>array(
+                    'desc'=>__('Enables attachments on tickets, regardless of channel'),
+                ),
+                'validators' => function($self, $value) {
+                    if (!ini_get('file_uploads'))
+                        $self->addError(__('The "file_uploads" directive is disabled in php.ini'));
+                }
+            )),
+        )
+        + $fileupload_config;
+    }
+
+    function isAttachmentsEnabled() {
+        $config = $this->getConfiguration();
+        return $config['attachments'];
     }
 }
 
@@ -929,7 +1186,11 @@ class PriorityField extends ChoiceField {
         return $this->to_php(null, $id);
     }
 
-    function to_php($value, $id) {
+    function to_php($value, $id=false) {
+        if (is_array($id)) {
+            reset($id);
+            $id = key($id);
+        }
         return Priority::lookup($id);
     }
 
@@ -943,6 +1204,155 @@ class PriorityField extends ChoiceField {
         return ($value instanceof Priority) ? $value->getDesc() : $value;
     }
 
+    function searchable($value) {
+        // Priority isn't searchable this way
+        return null;
+    }
+
+    function getConfigurationOptions() {
+        return array(
+            'prompt' => new TextboxField(array(
+                'id'=>2, 'label'=>__('Prompt'), 'required'=>false, 'default'=>'',
+                'hint'=>__('Leading text shown before a value is selected'),
+                'configuration'=>array('size'=>40, 'length'=>40),
+            )),
+        );
+    }
+}
+FormField::addFieldTypes(/*@trans*/ 'Dynamic Fields', function() {
+    return array(
+        'priority' => array(__('Priority Level'), PriorityField),
+    );
+});
+
+
+class TicketStateField extends ChoiceField {
+
+    static $_states = array(
+            'open' => array(
+                'name' => /* @trans, @context "ticket state name" */ 'Open',
+                'verb' => /* @trans, @context "ticket state action" */ 'Open'
+                ),
+            'closed' => array(
+                'name' => /* @trans, @context "ticket state name" */ 'Closed',
+                'verb' => /* @trans, @context "ticket state action" */ 'Close'
+                )
+            );
+    // Private states
+    static $_privatestates = array(
+            'archived' => array(
+                'name' => /* @trans, @context "ticket state name" */ 'Archived',
+                'verb' => /* @trans, @context "ticket state action" */ 'Archive'
+                ),
+            'deleted'  => array(
+                'name' => /* @trans, @context "ticket state name" */ 'Deleted',
+                'verb' => /* @trans, @context "ticket state action" */ 'Delete'
+                )
+            );
+
+    function hasIdValue() {
+        return true;
+    }
+
+    function isChangeable() {
+        return false;
+    }
+
+    function getChoices() {
+        static $_choices;
+
+        if (!isset($_choices)) {
+            // Translate and cache the choices
+            foreach (static::$_states as $k => $v)
+                $_choices[$k] =  _P('ticket state name', $v['name']);
+
+            $this->ht['default'] =  '';
+        }
+
+        return $_choices;
+    }
+
+    function getChoice($state) {
+
+        if ($state && is_array($state))
+            $state = key($state);
+
+        if (isset(static::$_states[$state]))
+            return _P('ticket state name', static::$_states[$state]['name']);
+
+        if (isset(static::$_privatestates[$state]))
+            return _P('ticket state name', static::$_privatestates[$state]['name']);
+
+        return $state;
+    }
+
+    function getConfigurationOptions() {
+        return array(
+            'prompt' => new TextboxField(array(
+                'id'=>2, 'label'=> __('Prompt'), 'required'=>false, 'default'=>'',
+                'hint'=> __('Leading text shown before a value is selected'),
+                'configuration'=>array('size'=>40, 'length'=>40),
+            )),
+        );
+    }
+
+    static function getVerb($state) {
+
+        if (isset(static::$_states[$state]))
+            return _P('ticket state action', static::$_states[$state]['verb']);
+
+        if (isset(static::$_privatestates[$state]))
+            return _P('ticket state action', static::$_privatestates[$state]['verb']);
+    }
+}
+FormField::addFieldTypes('Dynamic Fields', function() {
+    return array(
+        'state' => array('Ticket State', TicketStateField, false),
+    );
+});
+
+class TicketFlagField extends ChoiceField {
+
+    // Supported flags (TODO: move to configurable custom list)
+    static $_flags = array(
+            'onhold' => array(
+                'flag' => 1,
+                'name' => 'Onhold',
+                'states' => array('open'),
+                ),
+            'overdue' => array(
+                'flag' => 2,
+                'name' => 'Overdue',
+                'states' => array('open'),
+                ),
+            'answered' => array(
+                'flag' => 4,
+                'name' => 'Answered',
+                'states' => array('open'),
+                )
+            );
+
+    var $_choices;
+
+    function hasIdValue() {
+        return true;
+    }
+
+    function isChangeable() {
+        return true;
+    }
+
+    function getChoices() {
+        $this->ht['default'] =  '';
+
+        if (!$this->_choices) {
+            foreach (static::$_flags as $k => $v)
+                $this->_choices[$k] = $v['name'];
+        }
+
+        return $this->_choices;
+    }
+
     function getConfigurationOptions() {
         return array(
             'prompt' => new TextboxField(array(
@@ -953,17 +1363,296 @@ class PriorityField extends ChoiceField {
         );
     }
 }
-FormField::addFieldTypes('Built-in Lists', function() {
+
+FormField::addFieldTypes('Dynamic Fields', function() {
     return array(
-        'priority' => array('Priority Level', PriorityField),
+        'flags' => array('Ticket Flags', TicketFlagField, false),
     );
 });
 
+class FileUploadField extends FormField {
+    static $widget = 'FileUploadWidget';
+
+    protected $attachments;
+
+    static function getFileTypes() {
+        static $filetypes;
+
+        if (!isset($filetypes))
+            $filetypes = YamlDataParser::load(INCLUDE_DIR . '/config/filetype.yaml');
+        return $filetypes;
+    }
+
+    function getConfigurationOptions() {
+        // Compute size selections
+        $sizes = array('262144' => '— '.__('Small').' —');
+        $next = 512 << 10;
+        $max = strtoupper(ini_get('upload_max_filesize'));
+        $limit = (int) $max;
+        if (!$limit) $limit = 2 << 20; # 2M default value
+        elseif (strpos($max, 'K')) $limit <<= 10;
+        elseif (strpos($max, 'M')) $limit <<= 20;
+        elseif (strpos($max, 'G')) $limit <<= 30;
+        while ($next <= $limit) {
+            // Select the closest, larger value (in case the
+            // current value is between two)
+            $sizes[$next] = Format::file_size($next);
+            $next *= 2;
+        }
+        // Add extra option if top-limit in php.ini doesn't fall
+        // at a power of two
+        if ($next < $limit * 2)
+            $sizes[$limit] = Format::file_size($limit);
+
+        $types = array();
+        foreach (self::getFileTypes() as $type=>$info) {
+            $types[$type] = $info['description'];
+        }
+
+        global $cfg;
+        return array(
+            'size' => new ChoiceField(array(
+                'label'=>__('Maximum File Size'),
+                'hint'=>__('Choose maximum size of a single file uploaded to this field'),
+                'default'=>$cfg->getMaxFileSize(),
+                'choices'=>$sizes
+            )),
+            'mimetypes' => new ChoiceField(array(
+                'label'=>__('Restrict by File Type'),
+                'hint'=>__('Optionally, choose acceptable file types.'),
+                'required'=>false,
+                'choices'=>$types,
+                'configuration'=>array('multiselect'=>true,'prompt'=>__('No restrictions'))
+            )),
+            'extensions' => new TextareaField(array(
+                'label'=>__('Additional File Type Filters'),
+                'hint'=>__('Optionally, enter comma-separated list of additional file types, by extension. (e.g .doc, .pdf).'),
+                'configuration'=>array('html'=>false, 'rows'=>2),
+            )),
+            'max' => new TextboxField(array(
+                'label'=>__('Maximum Files'),
+                'hint'=>__('Users cannot upload more than this many files.'),
+                'default'=>false,
+                'required'=>false,
+                'validator'=>'number',
+                'configuration'=>array('size'=>8, 'length'=>4, 'placeholder'=>__('No limit')),
+            ))
+        );
+    }
+
+    function hasSpecialSearch() {
+        return false;
+    }
+
+    /**
+     * Called from the ajax handler for async uploads via web clients.
+     */
+    function ajaxUpload($bypass=false) {
+        $config = $this->getConfiguration();
+
+        $files = AttachmentFile::format($_FILES['upload'],
+            // For numeric fields assume configuration exists
+            !is_numeric($this->get('id')));
+        if (count($files) != 1)
+            Http::response(400, 'Send one file at a time');
+        $file = array_shift($files);
+        $file['name'] = urldecode($file['name']);
+
+        if (!$bypass && !$this->isValidFileType($file['name'], $file['type']))
+            Http::response(415, 'File type is not allowed');
+
+        $config = $this->getConfiguration();
+        if (!$bypass && $file['size'] > $config['size'])
+            Http::response(413, 'File is too large');
+
+        if (!($id = AttachmentFile::upload($file)))
+            Http::response(500, 'Unable to store file: '. $file['error']);
+
+        return $id;
+    }
+
+    /**
+     * Called from FileUploadWidget::getValue() when manual upload is used
+     * for browsers which do not support the HTML5 way of uploading async.
+     */
+    function uploadFile($file) {
+        if (!$this->isValidFileType($file['name'], $file['type']))
+            throw new FileUploadError(__('File type is not allowed'));
+
+        $config = $this->getConfiguration();
+        if ($file['size'] > $config['size'])
+            throw new FileUploadError(__('File size is too large'));
+
+        return AttachmentFile::upload($file);
+    }
+
+    /**
+     * Called from API and email routines and such to handle attachments
+     * sent other than via web upload
+     */
+    function uploadAttachment(&$file) {
+        if (!$this->isValidFileType($file['name'], $file['type']))
+            throw new FileUploadError(__('File type is not allowed'));
+
+        if (is_callable($file['data']))
+            $file['data'] = $file['data']();
+        if (!isset($file['size'])) {
+            // bootstrap.php include a compat version of mb_strlen
+            if (extension_loaded('mbstring'))
+                $file['size'] = mb_strlen($file['data'], '8bit');
+            else
+                $file['size'] = strlen($file['data']);
+        }
+
+        $config = $this->getConfiguration();
+        if ($file['size'] > $config['size'])
+            throw new FileUploadError(__('File size is too large'));
+
+        if (!$id = AttachmentFile::save($file))
+            throw new FileUploadError(__('Unable to save file'));
+
+        return $id;
+    }
+
+    function isValidFileType($name, $type=false) {
+        $config = $this->getConfiguration();
+
+        // Check MIME type - file ext. shouldn't be solely trusted.
+        if ($type && $config['__mimetypes']
+                && in_array($type, $config['__mimetypes']))
+            return true;
+
+        // Return true if all file types are allowed (.*)
+        if (!$config['__extensions'] || in_array('.*', $config['__extensions']))
+            return true;
+
+        $allowed = $config['__extensions'];
+        $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
+
+        return ($ext && is_array($allowed) && in_array(".$ext", $allowed));
+    }
+
+    function getFiles() {
+        if (!isset($this->attachments) && ($a = $this->getAnswer())
+            && ($e = $a->getEntry()) && ($e->get('id'))
+        ) {
+            $this->attachments = new GenericAttachments(
+                // Combine the field and entry ids to make the key
+                sprintf('%u', crc32('E'.$this->get('id').$e->get('id'))),
+                'E');
+        }
+        return $this->attachments ? $this->attachments->getAll() : array();
+    }
+
+    function getConfiguration() {
+        $config = parent::getConfiguration();
+        $_types = self::getFileTypes();
+        $mimetypes = array();
+        $extensions = array();
+        if (isset($config['mimetypes']) && is_array($config['mimetypes'])) {
+            foreach ($config['mimetypes'] as $type=>$desc) {
+                foreach ($_types[$type]['types'] as $mime=>$exts) {
+                    $mimetypes[$mime] = true;
+                    if (is_array($exts))
+                        foreach ($exts as $ext)
+                            $extensions['.'.$ext] = true;
+                }
+            }
+        }
+        if (strpos($config['extensions'], '.*') !== false)
+            $config['extensions'] = '';
+
+        if (is_string($config['extensions'])) {
+            foreach (preg_split('/\s+/', str_replace(',',' ', $config['extensions'])) as $ext) {
+                if (!$ext) {
+                    continue;
+                }
+                elseif (strpos($ext, '/')) {
+                    $mimetypes[$ext] = true;
+                }
+                else {
+                    if ($ext[0] != '.')
+                        $ext = '.' . $ext;
+                    // Add this to the MIME types list so it can be exported to
+                    // the @accept attribute
+                    if (!isset($extensions[$ext]))
+                        $mimetypes[$ext] = true;
+
+                    $extensions[$ext] = true;
+                }
+            }
+            $config['__extensions'] = array_keys($extensions);
+        }
+        elseif (is_array($config['extensions'])) {
+            $config['__extensions'] = $config['extensions'];
+        }
+
+        // 'mimetypes' is the array represented from the user interface,
+        // '__mimetypes' is a complete list of supported MIME types.
+        $config['__mimetypes'] = array_keys($mimetypes);
+        return $config;
+    }
+
+    // When the field is saved to database, encode the ID listing as a json
+    // array. Then, inspect the difference between the files actually
+    // attached to this field
+    function to_database($value) {
+        $this->getFiles();
+        if (isset($this->attachments)) {
+            $ids = array();
+            // Handle deletes
+            foreach ($this->attachments->getAll() as $f) {
+                if (!in_array($f['id'], $value))
+                    $this->attachments->delete($f['id']);
+                else
+                    $ids[] = $f['id'];
+            }
+            // Handle new files
+            foreach ($value as $id) {
+                if (!in_array($id, $ids))
+                    $this->attachments->upload($id);
+            }
+        }
+        return JsonDataEncoder::encode($value);
+    }
+
+    function parse($value) {
+        // Values in the database should be integer file-ids
+        return array_map(function($e) { return (int) $e; },
+            $value ?: array());
+    }
+
+    function to_php($value) {
+        return JsonDataParser::decode($value);
+    }
+
+    function display($value) {
+        $links = array();
+        foreach ($this->getFiles() as $f) {
+            $hash = strtolower($f['key']
+                . md5($f['id'].session_id().strtolower($f['key'])));
+            $links[] = sprintf('<a class="no-pjax" href="file.php?h=%s">%s</a>',
+                $hash, Format::htmlchars($f['name']));
+        }
+        return implode('<br/>', $links);
+    }
+
+    function toString($value) {
+        $files = array();
+        foreach ($this->getFiles() as $f) {
+            $files[] = $f['name'];
+        }
+        return implode(', ', $files);
+    }
+}
+
 class Widget {
+    static $media = null;
 
     function __construct($field) {
         $this->field = $field;
         $this->name = $field->getFormName();
+        $this->id = '_' . $this->name;
     }
 
     function parseValue() {
@@ -983,12 +1672,24 @@ class Widget {
             return $data[$this->field->get('name')];
         return null;
     }
+
+    /**
+     * getJsValueGetter
+     *
+     * Used with the dependent fields feature, this function should return a
+     * single javascript expression which can be used in a larger expression
+     * (<> == true, where <> is the result of this function). The %s token
+     * will be replaced with a jQuery variable representing this widget.
+     */
+    function getJsValueGetter() {
+        return '%s.val()';
+    }
 }
 
 class TextboxWidget extends Widget {
     static $input_type = 'text';
 
-    function render() {
+    function render($mode=false) {
         $config = $this->field->getConfiguration();
         if (isset($config['size']))
             $size = "size=\"{$config['size']}\"";
@@ -1003,7 +1704,7 @@ class TextboxWidget extends Widget {
         ?>
         <span style="display:inline-block">
         <input type="<?php echo static::$input_type; ?>"
-            id="<?php echo $this->name; ?>"
+            id="<?php echo $this->id; ?>"
             <?php echo implode(' ', array_filter(array(
                 $size, $maxlength, $classes, $autocomplete, $disabled)))
                 .' placeholder="'.$config['placeholder'].'"'; ?>
@@ -1028,7 +1729,7 @@ class PasswordWidget extends TextboxWidget {
 }
 
 class TextareaWidget extends Widget {
-    function render() {
+    function render($mode=false) {
         $config = $this->field->getConfiguration();
         $class = $cols = $rows = $maxlength = "";
         if (isset($config['rows']))
@@ -1038,13 +1739,16 @@ class TextareaWidget extends Widget {
         if (isset($config['length']) && $config['length'])
             $maxlength = "maxlength=\"{$config['length']}\"";
         if (isset($config['html']) && $config['html']) {
-            $class = 'class="richtext no-bar small"';
+            $class = array('richtext', 'no-bar');
+            $class[] = @$config['size'] ?: 'small';
+            $class = sprintf('class="%s"', implode(' ', $class));
             $this->value = Format::viewableImages($this->value);
         }
         ?>
         <span style="display:inline-block;width:100%">
         <textarea <?php echo $rows." ".$cols." ".$maxlength." ".$class
                 .' placeholder="'.$config['placeholder'].'"'; ?>
+            id="<?php echo $this->id; ?>"
             name="<?php echo $this->name; ?>"><?php
                 echo Format::htmlchars($this->value);
             ?></textarea>
@@ -1054,15 +1758,15 @@ class TextareaWidget extends Widget {
 }
 
 class PhoneNumberWidget extends Widget {
-    function render() {
+    function render($mode=false) {
         $config = $this->field->getConfiguration();
         list($phone, $ext) = explode("X", $this->value);
         ?>
-        <input type="text" name="<?php echo $this->name; ?>" value="<?php
+        <input id="<?php echo $this->id; ?>" type="text" name="<?php echo $this->name; ?>" value="<?php
         echo Format::htmlchars($phone); ?>"/><?php
         // Allow display of extension field even if disabled if the phone
         // number being edited has an extension
-        if ($ext || $config['ext']) { ?> Ext:
+        if ($ext || $config['ext']) { ?> <?php echo __('Ext'); ?>:
             <input type="text" name="<?php
             echo $this->name; ?>-ext" value="<?php echo Format::htmlchars($ext);
                 ?>" size="5"/>
@@ -1082,46 +1786,104 @@ class PhoneNumberWidget extends Widget {
 }
 
 class ChoicesWidget extends Widget {
+    static $media = array(
+        'css' => array(
+            '/css/jquery.multiselect.css',
+        ),
+    );
+
     function render($mode=false) {
+
+        if ($mode == 'view') {
+            if (!($val = (string) $this->field))
+                $val = sprintf('<span class="faded">%s</span>', __('None'));
+
+            echo $val;
+            return;
+        }
+
         $config = $this->field->getConfiguration();
+        if ($mode == 'search') {
+            $config['multiselect'] = true;
+        }
+
         // Determine the value for the default (the one listed if nothing is
         // selected)
-        $choices = $this->field->getChoices();
-        // We don't consider the 'default' when rendering in 'search' mode
+        $choices = $this->field->getChoices(true);
+        $prompt = $config['prompt'] ?: __('Select');
+
         $have_def = false;
-        if ($mode != 'search') {
+        // We don't consider the 'default' when rendering in 'search' mode
+        if (!strcasecmp($mode, 'search')) {
+            $def_val = $prompt;
+        } else {
             $def_key = $this->field->get('default');
             if (!$def_key && $config['default'])
                 $def_key = $config['default'];
+            if (is_array($def_key))
+                $def_key = key($def_key);
             $have_def = isset($choices[$def_key]);
-            if (!$have_def)
-                $def_val = ($config['prompt'])
-                   ? $config['prompt'] : 'Select';
-            else
-                $def_val = $choices[$def_key];
-        } else {
-            $def_val = ($config['prompt'])
-                ? $config['prompt'] : 'Select';
+            $def_val = $have_def ? $choices[$def_key] : $prompt;
+        }
+
+        $values = $this->value;
+        if (!is_array($values) && $values) {
+            $values = array($values => $this->field->getChoice($values));
         }
-        $value = $this->value;
-        if ($value === null && $have_def)
-            $value = $def_key;
-        ?> <span style="display:inline-block">
-        <select name="<?php echo $this->name; ?>">
-            <?php if (!$have_def) { ?>
+
+        if ($values === null)
+            $values = $have_def ? array($def_key => $choices[$def_key]) : array();
+
+        ?>
+        <select name="<?php echo $this->name; ?>[]"
+            id="<?php echo $this->id; ?>"
+            data-prompt="<?php echo $prompt; ?>"
+            <?php if ($config['multiselect'])
+                echo ' multiple="multiple" class="multiselect"'; ?>>
+            <?php if (!$have_def && !$config['multiselect']) { ?>
             <option value="<?php echo $def_key; ?>">&mdash; <?php
                 echo $def_val; ?> &mdash;</option>
             <?php }
-            foreach ($choices as $key=>$name) {
+            foreach ($choices as $key => $name) {
                 if (!$have_def && $key == $def_key)
                     continue; ?>
                 <option value="<?php echo $key; ?>" <?php
-                    if ($value == $key) echo 'selected="selected"';
+                    if (isset($values[$key])) echo 'selected="selected"';
                 ?>><?php echo $name; ?></option>
             <?php } ?>
         </select>
-        </span>
         <?php
+        if ($config['multiselect']) {
+         ?>
+        <script type="text/javascript">
+        $(function() {
+            $("#<?php echo $this->id; ?>")
+            .multiselect({'noneSelectedText':'<?php echo $prompt; ?>'});
+        });
+        </script>
+       <?php
+        }
+    }
+
+    function getValue() {
+        $value = parent::getValue();
+
+        if (!$value) return null;
+
+        // Assume multiselect
+        $values = array();
+        $choices = $this->field->getChoices();
+        if (is_array($value)) {
+            foreach($value as $k => $v) {
+                if (isset($choices[$v]))
+                    $values[$v] = $choices[$v];
+            }
+        }
+        return $values;
+    }
+
+    function getJsValueGetter() {
+        return '%s.find(":selected").val()';
     }
 }
 
@@ -1131,12 +1893,13 @@ class CheckboxWidget extends Widget {
         $this->name = '_field-checkboxes';
     }
 
-    function render() {
+    function render($mode=false) {
         $config = $this->field->getConfiguration();
         if (!isset($this->value))
             $this->value = $this->field->get('default');
         ?>
-        <input style="vertical-align:top;" type="checkbox" name="<?php echo $this->name; ?>[]" <?php
+        <input id="<?php echo $this->id; ?>" style="vertical-align:top;"
+            type="checkbox" name="<?php echo $this->name; ?>[]" <?php
             if ($this->value) echo 'checked="checked"'; ?> value="<?php
             echo $this->field->get('id'); ?>"/>
         <?php
@@ -1152,10 +1915,14 @@ class CheckboxWidget extends Widget {
             return @in_array($this->field->get('id'), $data[$this->name]);
         return parent::getValue();
     }
+
+    function getJsValueGetter() {
+        return '%s.is(":checked")';
+    }
 }
 
 class DatetimePickerWidget extends Widget {
-    function render() {
+    function render($mode=false) {
         global $cfg;
 
         $config = $this->field->getConfiguration();
@@ -1167,10 +1934,11 @@ class DatetimePickerWidget extends Widget {
                     $_SESSION['TZ_OFFSET']+($_SESSION['TZ_DST']?date('I',$this->value):0);
 
             list($hr, $min) = explode(':', date('H:i', $this->value));
-            $this->value = date($cfg->getDateFormat(), $this->value);
+            $this->value = Format::date($cfg->getDateFormat(), $this->value);
         }
         ?>
         <input type="text" name="<?php echo $this->name; ?>"
+            id="<?php echo $this->id; ?>"
             value="<?php echo Format::htmlchars($this->value); ?>" size="12"
             autocomplete="off" class="dp" />
         <script type="text/javascript">
@@ -1188,7 +1956,7 @@ class DatetimePickerWidget extends Widget {
                     showButtonPanel: true,
                     buttonImage: './images/cal.png',
                     showOn:'both',
-                    dateFormat: $.translate_format('<?php echo $cfg->getDateFormat(); ?>'),
+                    dateFormat: $.translate_format('<?php echo $cfg->getDateFormat(); ?>')
                 });
             });
         </script>
@@ -1225,7 +1993,7 @@ class DatetimePickerWidget extends Widget {
 }
 
 class SectionBreakWidget extends Widget {
-    function render() {
+    function render($mode=false) {
         ?><div class="form-header section-break"><h3><?php
         echo Format::htmlchars($this->field->get('label'));
         ?></h3><em><?php echo Format::htmlchars($this->field->get('hint'));
@@ -1253,27 +2021,319 @@ class ThreadEntryWidget extends Widget {
             cols="21" rows="8" style="width:80%;"><?php echo
             Format::htmlchars($this->value); ?></textarea>
     <?php
+        $config = $this->field->getConfiguration();
+        if (!$config['attachments'])
+            return;
+
+        $attachments = $this->getAttachments($config);
+        print $attachments->render($client);
+        foreach ($attachments->getMedia() as $type=>$urls) {
+            foreach ($urls as $url)
+                Form::emitMedia($url, $type);
+        }
     }
 
-    function showAttachments($errors=array()) {
-        global $cfg, $thisclient;
+    function getAttachments($config=false) {
+        if (!$config)
+            $config = $this->field->getConfiguration();
 
-        if(($cfg->allowOnlineAttachments()
-            && !$cfg->allowAttachmentsOnlogin())
-            || ($cfg->allowAttachmentsOnlogin()
-                && ($thisclient && $thisclient->isValid()))) { ?>
-        <div class="clear"></div>
-        <hr/>
-        <div><strong style="padding-right:1em;vertical-align:top">Attachments: </strong>
-        <div style="display:inline-block">
-        <div class="uploads" style="display:block"></div>
-        <input type="file" class="multifile" name="attachments[]" id="attachments" size="30" value="" />
-        </div>
-        <font class="error">&nbsp;<?php echo $errors['attachments']; ?></font>
+        $field = new FileUploadField(array(
+            'id'=>'attach',
+            'name'=>'attach:' . $this->field->get('id'),
+            'configuration'=>$config)
+        );
+        $field->setForm($this->field->getForm());
+        return $field;
+    }
+}
+
+class FileUploadWidget extends Widget {
+    static $media = array(
+        'css' => array(
+            '/css/filedrop.css',
+        ),
+    );
+
+    function render($how) {
+        $config = $this->field->getConfiguration();
+        $name = $this->field->getFormName();
+        $id = substr(md5(spl_object_hash($this)), 10);
+        $attachments = $this->field->getFiles();
+        $mimetypes = array_filter($config['__mimetypes'],
+            function($t) { return strpos($t, '/') !== false; }
+        );
+        $files = array();
+        foreach ($this->value ?: array() as $fid) {
+            $found = false;
+            foreach ($attachments as $f) {
+                if ($f['id'] == $fid) {
+                    $files[] = $f;
+                    $found = true;
+                    break;
+                }
+            }
+            if (!$found && ($file = AttachmentFile::lookup($fid))) {
+                $files[] = array(
+                    'id' => $file->getId(),
+                    'name' => $file->getName(),
+                    'type' => $file->getType(),
+                    'size' => $file->getSize(),
+                );
+            }
+        }
+        ?><div id="<?php echo $id;
+            ?>" class="filedrop"><div class="files"></div>
+            <div class="dropzone"><i class="icon-upload"></i>
+            <?php echo sprintf(
+                __('Drop files here or %s choose them %s'),
+                '<a href="#" class="manual">', '</a>'); ?>
+        <input type="file" multiple="multiple"
+            id="file-<?php echo $id; ?>" style="display:none;"
+            accept="<?php echo implode(',', $config['__mimetypes']); ?>"/>
+        </div></div>
+        <script type="text/javascript">
+        $(function(){$('#<?php echo $id; ?> .dropzone').filedropbox({
+          url: 'ajax.php/form/upload/<?php echo $this->field->get('id') ?>',
+          link: $('#<?php echo $id; ?>').find('a.manual'),
+          paramname: 'upload[]',
+          fallback_id: 'file-<?php echo $id; ?>',
+          allowedfileextensions: <?php echo JsonDataEncoder::encode(
+            $config['__extensions'] ?: array()); ?>,
+          allowedfiletypes: <?php echo JsonDataEncoder::encode(
+            $mimetypes); ?>,
+          maxfiles: <?php echo $config['max'] ?: 20; ?>,
+          maxfilesize: <?php echo ($config['size'] ?: 1048576) / 1048576; ?>,
+          name: '<?php echo $name; ?>[]',
+          files: <?php echo JsonDataEncoder::encode($files); ?>
+        });});
+        </script>
+<?php
+    }
+
+    function getValue() {
+        $data = $this->field->getSource();
+        $ids = array();
+        // Handle manual uploads (IE<10)
+        if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES[$this->name])) {
+            foreach (AttachmentFile::format($_FILES[$this->name]) as $file) {
+                try {
+                    $ids[] = $this->field->uploadFile($file);
+                }
+                catch (FileUploadError $ex) {}
+            }
+            return array_merge($ids, parent::getValue() ?: array());
+        }
+        // If no value was sent, assume an empty list
+        elseif ($data && is_array($data) && !isset($data[$this->name]))
+            return array();
+
+        return parent::getValue();
+    }
+}
+
+class FileUploadError extends Exception {}
+
+class FreeTextField extends FormField {
+    static $widget = 'FreeTextWidget';
+
+    function getConfigurationOptions() {
+        return array(
+            'content' => new TextareaField(array(
+                'configuration' => array('html' => true, 'size'=>'large'),
+                'label'=>__('Content'), 'required'=>true, 'default'=>'',
+                'hint'=>__('Free text shown in the form, such as a disclaimer'),
+            )),
+        );
+    }
+
+    function hasData() {
+        return false;
+    }
+
+    function isBlockLevel() {
+        return true;
+    }
+}
+
+class FreeTextWidget extends Widget {
+    function render($mode=false) {
+        $config = $this->field->getConfiguration();
+        ?><div class=""><h3><?php
+            echo Format::htmlchars($this->field->get('label'));
+        ?></h3><em><?php
+            echo Format::htmlchars($this->field->get('hint'));
+        ?></em><div><?php
+            echo Format::viewableImages($config['content']); ?></div>
         </div>
-        <hr/>
         <?php
+    }
+}
+
+class VisibilityConstraint {
+
+    const HIDDEN =      0x0001;
+    const VISIBLE =     0x0002;
+
+    var $initial;
+    var $constraint;
+
+    function __construct($constraint, $initial=self::VISIBLE) {
+        $this->constraint = $constraint;
+        $this->initial = $initial;
+    }
+
+    function emitJavascript($field) {
+        $func = 'recheck';
+        $form = $field->getForm();
+?>
+    <script type="text/javascript">
+      !(function() {
+        var <?php echo $func; ?> = function() {
+          var target = $('#field<?php echo $field->getWidget()->id; ?>');
+
+<?php   $fields = $this->getAllFields($this->constraint);
+        foreach ($fields as $f) {
+            $field = $form->getField($f);
+            echo sprintf('var %1$s = $("#%1$s");',
+                $field->getWidget()->id);
+        }
+        $expression = $this->compileQ($this->constraint, $form);
+?>
+          if (<?php echo $expression; ?>)
+            target.slideDown('fast', function (){
+                $(this).trigger('show');
+                });
+          else
+            target.slideUp('fast', function (){
+                $(this).trigger('hide');
+                });
+        };
+
+<?php   foreach ($fields as $f) {
+            $w = $form->getField($f)->getWidget();
+?>
+        $('#<?php echo $w->id; ?>').on('change', <?php echo $func; ?>);
+        $('#field<?php echo $w->id; ?>').on('show hide', <?php
+                echo $func; ?>);
+<?php   } ?>
+      })();
+    </script><?php
+    }
+
+    /**
+     * Determines if the field was visible when the form was submitted
+     */
+    function isVisible($field) {
+        return $this->compileQPhp($this->constraint, $field);
+    }
+
+    function compileQPhp(Q $Q, $field) {
+        $form = $field->getForm();
+        $expr = array();
+        foreach ($Q->constraints as $c=>$value) {
+            if ($value instanceof Q) {
+                $expr[] = $this->compileQPhp($value, $field);
+            }
+            else {
+                @list($f, $op) = explode('__', $c, 2);
+                $field = $form->getField($f);
+                $wval = $field->getClean();
+                switch ($op) {
+                case 'eq':
+                case null:
+                    $expr[] = ($wval == $value && $field->isVisible());
+                }
+            }
+        }
+        $glue = $Q->isOred()
+            ? function($a, $b) { return $a || $b; }
+            : function($a, $b) { return $a && $b; };
+        $initial = !$Q->isOred();
+        $expression = array_reduce($expr, $glue, $initial);
+        if ($Q->isNegated)
+            $expression = !$expression;
+        return $expression;
+    }
+
+    function getAllFields(Q $Q, &$fields=array()) {
+        foreach ($Q->constraints as $c=>$value) {
+            if ($c instanceof Q) {
+                $this->getAllFields($c, $fields);
+            }
+            else {
+                list($f, $op) = explode('__', $c, 2);
+                $fields[$f] = true;
+            }
+        }
+        return array_keys($fields);
+    }
+
+    function compileQ($Q, $form) {
+        $expr = array();
+        foreach ($Q->constraints as $c=>$value) {
+            if ($value instanceof Q) {
+                $expr[] = $this->compileQ($value, $form);
+            }
+            else {
+                list($f, $op) = explode('__', $c, 2);
+                $widget = $form->getField($f)->getWidget();
+                $id = $widget->id;
+                switch ($op) {
+                case 'eq':
+                case null:
+                    $expr[] = sprintf('(%s.is(":visible") && %s)',
+                            $id,
+                            sprintf('%s == %s',
+                                sprintf($widget->getJsValueGetter(), $id),
+                                JsonDataEncoder::encode($value))
+                            );
+                }
+            }
         }
+        $glue = $Q->isOred() ? ' || ' : ' && ';
+        $expression = implode($glue, $expr);
+        if (count($expr) > 1)
+            $expression = '('.$expression.')';
+        if ($Q->isNegated)
+            $expression = '!'.$expression;
+        return $expression;
+    }
+}
+
+class Q {
+    const NEGATED = 0x0001;
+    const ANY =     0x0002;
+
+    var $constraints;
+    var $flags;
+    var $negated = false;
+    var $ored = false;
+
+    function __construct($filter, $flags=0) {
+        $this->constraints = $filter;
+        $this->negated = $flags & self::NEGATED;
+        $this->ored = $flags & self::ANY;
+    }
+
+    function isNegated() {
+        return $this->negated;
+    }
+
+    function isOred() {
+        return $this->ored;
+    }
+
+    function negate() {
+        $this->negated = !$this->negated;
+        return $this;
+    }
+
+    static function not(array $constraints) {
+        return new static($constraints, self::NEGATED);
+    }
+
+    static function any(array $constraints) {
+        return new static($constraints, self::ORED);
     }
 }
 
diff --git a/include/class.group.php b/include/class.group.php
index f8ce1d8213dfee372707a33e835460aa7e25e83e..67f392190f0cd357872ad9a1e550cf6258175631 100644
--- a/include/class.group.php
+++ b/include/class.group.php
@@ -183,16 +183,15 @@ class Group {
     }
 
     function save($id,$vars,&$errors) {
-
         if($id && $vars['id']!=$id)
-            $errors['err']='Missing or invalid group ID';
+            $errors['err']=__('Missing or invalid group ID');
             
         if(!$vars['name']) {
-            $errors['name']='Group name required';
+            $errors['name']=__('Group name required');
         }elseif(strlen($vars['name'])<3) {
-            $errors['name']='Group name must be at least 3 chars.';
+            $errors['name']=__('Group name must be at least 3 chars.');
         }elseif(($gid=Group::getIdByName($vars['name'])) && $gid!=$id){
-            $errors['name']='Group name already exists';
+            $errors['name']=__('Group name already exists');
         }
         
         if($errors) return false;
@@ -219,16 +218,18 @@ class Group {
             if(($res=db_query($sql)))
                 return true;
 
-            $errors['err']='Unable to update group. Internal error occurred.';
-            
+            $errors['err']=sprintf(__('Unable to update %s.'), __('this group'))
+               .' '.__('Internal error occurred');
+
         }else{
             $sql='INSERT INTO '.GROUP_TABLE.' '.$sql.',created=NOW()';
             if(($res=db_query($sql)) && ($id=db_insert_id()))
                 return $id;
-                
-            $errors['err']='Unable to create the group. Internal error';
+
+            $errors['err']=sprintf(__('Unable to create %s.'), __('this group'))
+               .' '.__('Internal error occurred');
         }
-        
+
         return false;
     }
 }
diff --git a/include/class.i18n.php b/include/class.i18n.php
index 1158e244964ef4766bf79fdf108a96e6971aa85a..4a9540520c26816b0c405ccfe318402ecf6ef840 100644
--- a/include/class.i18n.php
+++ b/include/class.i18n.php
@@ -29,8 +29,10 @@ class Internationalization {
         if ($cfg && ($lang = $cfg->getSystemLanguage()))
             array_unshift($this->langs, $language);
 
-        if ($language)
-            array_unshift($this->langs, $language);
+        // Detect language filesystem path, case insensitively
+        if ($language && ($info = self::getLanguageInfo($language))) {
+            array_unshift($this->langs, $info['code']);
+        }
     }
 
     function getTemplate($path) {
@@ -50,6 +52,7 @@ class Internationalization {
             'department.yaml' =>    'Dept::create',
             'sla.yaml' =>           'SLA::create',
             'form.yaml' =>          'DynamicForm::create',
+            'list.yaml' =>          'DynamicList::create',
             // Note that department, sla, and forms are required for
             // help_topic
             'help_topic.yaml' =>    'Topic::create',
@@ -57,9 +60,12 @@ class Internationalization {
             'team.yaml' =>          'Team::create',
             // Organization
             'organization.yaml' =>  'Organization::__create',
+            // Ticket
+            'ticket_status.yaml' =>  'TicketStatus::__create',
             // Note that group requires department
             'group.yaml' =>         'Group::create',
             'file.yaml' =>          'AttachmentFile::create',
+            'sequence.yaml' =>      'Sequence::__create',
         );
 
         $errors = array();
@@ -68,6 +74,9 @@ class Internationalization {
                 foreach ($objects as $o) {
                     if ($m && is_callable($m))
                         @call_user_func_array($m, array($o, &$errors));
+                    // TODO: Add a warning to the success page for errors
+                    //       found here
+                    $errors = array();
                 }
             }
         }
@@ -159,14 +168,43 @@ class Internationalization {
     }
 
     static function getLanguageDescription($lang) {
+        global $thisstaff, $thisclient;
+
         $langs = self::availableLanguages();
         $lang = strtolower($lang);
-        if (isset($langs[$lang]))
-            return $langs[$lang]['desc'];
+        if (isset($langs[$lang])) {
+            $info = &$langs[$lang];
+            if (!isset($info['desc'])) {
+                if (extension_loaded('intl')) {
+                    $lang = self::getCurrentLanguage();
+                    list($simple_lang,) = explode('_', $lang);
+                    $info['desc'] = sprintf("%s%s",
+                        // Display the localized name of the language
+                        Locale::getDisplayName($info['code'], $info['code']),
+                        // If the major language differes from the user's,
+                        // display the language in the user's language
+                        (strpos($simple_lang, $info['lang']) === false
+                            ? sprintf(' (%s)', Locale::getDisplayName($info['code'], $lang)) : '')
+                    );
+                }
+                else {
+                    $info['desc'] = sprintf("%s%s (%s)",
+                        $info['nativeName'],
+                        $info['locale'] ? sprintf(' - %s', $info['locale']) : '',
+                        $info['name']);
+                }
+            }
+            return $info['desc'];
+        }
         else
             return $lang;
     }
 
+    static function getLanguageInfo($lang) {
+        $langs = self::availableLanguages();
+        return @$langs[strtolower($lang)] ?: array();
+    }
+
     static function availableLanguages($base=I18N_DIR) {
         static $cache = false;
         if ($cache) return $cache;
@@ -187,11 +225,8 @@ class Internationalization {
                     'lang' => $code,
                     'locale' => $locale,
                     'path' => $f,
+                    'phar' => substr($f, -5) == '.phar',
                     'code' => $base,
-                    'desc' => sprintf("%s%s (%s)",
-                        $langs[$code]['nativeName'],
-                        $locale ? sprintf(' - %s', $locale) : '',
-                        $langs[$code]['name']),
                 );
             }
         }
@@ -285,6 +320,84 @@ class Internationalization {
 
         return $best_match_langcode;
     }
+
+    static function getCurrentLanguage($user=false) {
+        global $thisstaff, $thisclient;
+
+        $user = $user ?: $thisstaff ?: $thisclient;
+        if ($user && method_exists($user, 'getLanguage'))
+            return $user->getLanguage();
+        if (isset($_SESSION['client:lang']))
+            return $_SESSION['client:lang'];
+        return self::getDefaultLanguage();
+    }
+
+    static function getTtfFonts() {
+        if (!class_exists('Phar'))
+            return;
+        $fonts = $subs = array();
+        foreach (self::availableLanguages() as $code=>$info) {
+            if (!$info['phar'] || !isset($info['fonts']))
+                continue;
+            foreach ($info['fonts'] as $simple => $collection) {
+                foreach ($collection as $type => $name) {
+                    list($name, $url) = $name;
+                    $ttffile = 'phar://' . $info['path'] . '/fonts/' . $name;
+                    if (file_exists($ttffile))
+                        $fonts[$simple][$type] = $ttffile;
+                }
+                if (@$collection[':sub'])
+                    $subs[] = $simple;
+            }
+        }
+        $rv = array($fonts, $subs);
+        Signal::send('config.ttfonts', null, $rv);
+        return $rv;
+    }
+
+    static function bootstrap() {
+
+        require_once INCLUDE_DIR . 'class.translation.php';
+
+        $domain = 'messages';
+        TextDomain::setDefaultDomain($domain);
+        TextDomain::lookup()->setPath(I18N_DIR);
+
+        // User-specific translations
+        function _N($msgid, $plural, $n) {
+            return TextDomain::lookup()->getTranslation()
+                ->ngettext($msgid, $plural, is_numeric($n) ? $n : 1);
+        }
+
+        // System-specific translations
+        function _S($msgid) {
+            global $cfg;
+            return __($msgid);
+        }
+        function _NS($msgid, $plural, $count) {
+            global $cfg;
+        }
+
+        // Phrases with separate contexts
+        function _P($context, $msgid) {
+            return TextDomain::lookup()->getTranslation()
+                ->pgettext($context, $msgid);
+        }
+        function _NP($context, $singular, $plural, $n) {
+            return TextDomain::lookup()->getTranslation()
+                ->npgettext($context, $singular, $plural, is_numeric($n) ? $n : 1);
+        }
+
+        // Language-specific translations
+        function _L($msgid, $locale) {
+            return TextDomain::lookup()->getTranslation($locale)
+                ->translate($msgid);
+        }
+        function _NL($msgid, $plural, $n, $locale) {
+            return TextDomain::lookup()->getTranslation($locale)
+                ->ngettext($msgid, $plural, is_numeric($n) ? $n : 1);
+        }
+    }
 }
 
 class DataTemplate {
@@ -325,6 +438,14 @@ class DataTemplate {
         return $this->data;
     }
 
+    function getRawData() {
+        if (!isset($this->data) && $this->filepath)
+            return file_get_contents($this->filepath);
+            // TODO: If there was a parsing error, attempt to try the next
+            //       language in the list of requested languages
+        return false;
+    }
+
     function getLang() {
         return $this->lang;
     }
diff --git a/include/class.json.php b/include/class.json.php
index f3dfd282cdf565522c45b4f7f173da45e7e40bfa..ad5ac65c12e3da2c939faecdb67dfec3f049c635 100644
--- a/include/class.json.php
+++ b/include/class.json.php
@@ -43,19 +43,19 @@ class JsonDataParser {
     function lastError() {
         if (function_exists("json_last_error")) {
             $errors = array(
-            JSON_ERROR_NONE => 'No errors',
-            JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
-            JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch',
-            JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
-            JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
-            JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded'
+            JSON_ERROR_NONE => __('No errors'),
+            JSON_ERROR_DEPTH => __('Maximum stack depth exceeded'),
+            JSON_ERROR_STATE_MISMATCH => __('Underflow or the modes mismatch'),
+            JSON_ERROR_CTRL_CHAR => __('Unexpected control character found'),
+            JSON_ERROR_SYNTAX => __('Syntax error, malformed JSON'),
+            JSON_ERROR_UTF8 => __('Malformed UTF-8 characters, possibly incorrectly encoded')
             );
             if ($message = $errors[json_last_error()])
                 return $message;
-            return "Unknown error";
+            return __("Unknown error");
         } else {
             # Doesn't look like Servies_JSON supports errors for decode()
-            return "Unknown JSON parsing error";
+            return __("Unknown JSON parsing error");
         }
     }
 }
diff --git a/include/class.knowledgebase.php b/include/class.knowledgebase.php
new file mode 100644
index 0000000000000000000000000000000000000000..9ee15bdac03c431489c55adad7b800884b0b8a66
--- /dev/null
+++ b/include/class.knowledgebase.php
@@ -0,0 +1,155 @@
+<?php
+/*********************************************************************
+    class.knowledgebase.php
+
+    Backend support for knowledgebase creates, edits, deletes, and
+    attachments.
+
+    Copyright (c)  2006-2013 osTicket
+    http://www.osticket.com
+
+    Released under the GNU General Public License WITHOUT ANY WARRANTY.
+    See LICENSE.TXT for details.
+
+    vim: expandtab sw=4 ts=4 sts=4:
+**********************************************************************/
+require_once("class.file.php");
+
+class Knowledgebase {
+
+    function Knowledgebase($id) {
+        $res=db_query(
+            'SELECT title, isenabled, dept_id, created, updated '
+           .'FROM '.CANNED_TABLE.' WHERE canned_id='.db_input($id));
+        if (!$res || !db_num_rows($res)) return false;
+        list(   $this->title,
+                $this->enabled,
+                $this->department,
+                $this->created,
+                $this->updated) = db_fetch_row($res);
+        $this->id = $id;
+        $this->_attachments = new AttachmentList(
+            CANNED_ATTACHMENT_TABLE, 'canned_id='.db_input($id));
+    }
+
+    /* ------------------> Getter methods <--------------------- */
+    function getTitle() { return $this->title; }
+    function isEnabled() { return !!$this->enabled; }
+    function getAnswer() { 
+        if (!isset($this->answer)) {
+            if ($res=db_query('SELECT answer FROM '.CANNED_TABLE
+                    .' WHERE canned_id='.db_input($this->id))) {
+                list($this->answer)=db_fetch_row($res);
+            }
+        }
+        return $this->answer;
+    }
+    function getCreated() { return $this->created; }
+    function lastUpdated() { return $this->updated; }
+    function attachments() { return $this->_attachments; }
+    function getDeptId() { return $this->department; }
+    function getDepartment() { return new Dept($this->department); }
+    function getId() { return $this->id; }
+
+    /* ------------------> Setter methods <--------------------- */
+    function publish() { $this->published = true; }
+    function unpublish() { $this->published = false; }
+    function setPublished($val) { $this->published = !!$val; }
+    function setEnabled($val) { $this->enabled = !!$val; }
+    function setTitle($title) { $this->title = $title; }
+    function setKeywords($words) { $this->keywords = $words; }
+    function setAnswer($text) { $this->answer = $text; }
+    function setDepartment($id) { $this->department = $id; }
+
+    /* -------------> Validation and Clean methods <------------ */
+    function validate(&$errors, $what=null) {
+        if (!$what) $what=$this->getHashtable();
+        else $this->clean($what);
+        # TODO: Validate current values ($this->yada)
+        # Apply hashtable to this -- return error list
+        $validation = array(
+            'title' => array('is_string', __('Title is required'))
+        );
+        foreach ($validation as $key=>$details) {
+            list($func, $error) = $details;
+            if (!call_user_func($func, $what[$key])) {
+                $errors[$key] = $error;
+            }
+        }
+        return count($errors) == 0;
+    }
+
+    function clean(&$what) {
+        if (isset($what['topic']))
+            $what['topic']=Format::striptags(trim($what['topic']));
+    }
+
+    function getHashtable() {
+        # TODO: Return hashtable like the one that would be passed into
+        #       $this->save() or self::create()
+        return array('title'=>$this->title, 'department'=>$this->department,
+            'isenabled'=>$this->enabled);
+    }
+
+    /* -------------> Database access methods <----------------- */
+    function update() { 
+        if (!@$this->validate()) return false;
+        db_query(
+            'UPDATE '.CANNED_TABLE.' SET title='.db_input($this->title)
+                .', isenabled='.db_input($this->enabled)
+                .', dept_id='.db_input($this->department)
+                .', updated=NOW()'
+                .((isset($this->answer)) 
+                    ? ', answer='.db_input($this->answer) : '')
+                .' WHERE canned_id='.db_input($this->id));
+        return db_affected_rows() == 1;
+    }
+    function delete() {
+        db_query('DELETE FROM '.CANNED_TABLE.' WHERE canned_id='
+            .db_input($this->id));
+        return db_affected_rows() == 1;
+    }
+    /* For ->attach() and ->detach(), use $this->attachments() */
+    function attach($file) { return $this->_attachments->add($file); }
+    function detach($file) { return $this->_attachments->remove($file); }
+
+    /* ------------------> Static methods <--------------------- */
+    function create($hash, &$errors) {
+        if (!self::validate($hash, $errors)) return false;
+        db_query('INSERT INTO '.CANNED_TABLE
+            .' (title, answer, department, isenabled, created, updated) VALUES ('
+            .db_input($hash['title']).','
+            .db_input($hash['answer']).','
+            .db_input($hash['dept']).','
+            .db_input($hash['isenabled']).',NOW(),NOW()');
+        return db_insert_id();
+    }
+
+    function save($id, $new_stuff, &$errors) {
+        if (!$id) return self::create($new_stuff, $errors);
+        if (!self::validate($errors, $new_stuff)) return false;
+
+        # else
+        if (!($obj = new Knowledgebase($id))) { return false; }
+        $obj->setEnabled($new_stuff['enabled']);
+        $obj->setTitle($new_stuff['title']);
+        $obj->setAnswer($new_stuff['answer']);
+        $obj->setDepartment($new_stuff['dept']);
+
+        return $obj->update();
+    }
+
+    function findByTitle($title) {
+        $res=db_query('SELECT canned_id FROM '.CANNED_TABLE
+            .' WHERE title LIKE '.db_input($title));
+        if (list($id) = db_fetch_row($res)) {
+            return new Knowledgebase($id);
+        }
+        return false;
+    }
+
+    function lookup($id) {
+        return ($id && is_numeric($id) && ($obj= new Knowledgebase($id)) && $obj->getId()==$id)
+            ? $obj : null;
+    }
+}
diff --git a/include/class.list.php b/include/class.list.php
new file mode 100644
index 0000000000000000000000000000000000000000..dfee67d558d3ef2a56ac81f16e14fa8a090c82a7
--- /dev/null
+++ b/include/class.list.php
@@ -0,0 +1,1142 @@
+<?php
+/*********************************************************************
+    class.list.php
+
+    Custom List utils
+
+    Jared Hancock <jared@osticket.com>
+    Peter Rotich <peter@osticket.com>
+    Copyright (c)  2014 osTicket
+    http://www.osticket.com
+
+    Released under the GNU General Public License WITHOUT ANY WARRANTY.
+    See LICENSE.TXT for details.
+
+    vim: expandtab sw=4 ts=4 sts=4:
+**********************************************************************/
+
+require_once(INCLUDE_DIR .'class.dynamic_forms.php');
+
+/**
+ * Interface for Custom Lists
+ *
+ * Custom lists are used to represent list of arbitrary data that can be
+ * used as dropdown or typeahead selections in dynamic forms. This model
+ * defines a list. The individual items are stored in the "Item" model.
+ *
+ */
+
+interface CustomList {
+
+    function getId();
+    function getName();
+    function getPluralName();
+
+    function getNumItems();
+    function getAllItems();
+    function getItems($criteria);
+
+    function getItem($id);
+    function addItem($vars, &$errors);
+
+    function getForm(); // Config form
+    function hasProperties();
+
+    function getSortModes();
+    function getSortMode();
+    function getListOrderBy();
+
+    function allowAdd();
+    function hasAbbrev();
+
+    function update($vars, &$errors);
+    function delete();
+
+    static function create($vars, &$errors);
+    static function lookup($id);
+}
+
+/*
+ * Custom list item interface
+ */
+interface CustomListItem {
+    function getId();
+    function getValue();
+    function getAbbrev();
+    function getSortOrder();
+
+    function getConfiguration();
+    function getConfigurationForm($source=null);
+
+
+    function isEnabled();
+    function isDeletable();
+    function isEnableable();
+    function isInternal();
+
+    function enable();
+    function disable();
+
+    function update($vars, &$errors);
+    function delete();
+}
+
+
+/*
+ * Base class for Custom List handlers
+ *
+ * Custom list handler extends custom list and might store data outside the
+ * typical dynamic list store.
+ *
+ */
+
+abstract class CustomListHandler {
+
+    var $_list;
+
+    function __construct($list) {
+        $this->_list = $list;
+    }
+
+    function __call($name, $args) {
+
+        $rv = null;
+        if ($this->_list && is_callable(array($this->_list, $name)))
+            $rv = $args
+                ? call_user_func_array(array($this->_list, $name), $args)
+                : call_user_func(array($this->_list, $name));
+
+        return $rv;
+    }
+
+    function update($vars, &$errors) {
+        return $this->_list->update($vars, $errors);
+    }
+
+    abstract function getNumItems();
+    abstract function getAllItems();
+    abstract function getItems($criteria);
+    abstract function getItem($id);
+    abstract function addItem($vars, &$errors);
+}
+
+/**
+ * Dynamic lists are Custom Lists solely defined by the user.
+ *
+ */
+class DynamicList extends VerySimpleModel implements CustomList {
+
+    static $meta = array(
+        'table' => LIST_TABLE,
+        'ordering' => array('name'),
+        'pk' => array('id'),
+    );
+
+    // Required fields
+    static $fields = array('name', 'name_plural', 'sort_mode', 'notes');
+
+    // Supported masks
+    const MASK_EDIT     = 0x0001;
+    const MASK_ADD      = 0x0002;
+    const MASK_DELETE   = 0x0004;
+    const MASK_ABBREV   = 0x0008;
+
+    var $_items;
+    var $_form;
+    var $_config;
+
+    function __construct() {
+        call_user_func_array(array('parent', '__construct'), func_get_args());
+        $this->_config = new Config('list.'.$this->getId());
+    }
+
+    function getId() {
+        return $this->get('id');
+    }
+
+    function getInfo() {
+        return $this->ht;
+    }
+
+    function hasProperties() {
+        return ($this->getForm() && $this->getForm()->getFields());
+    }
+
+    function getSortModes() {
+        return array(
+            'Alpha'     => __('Alphabetical'),
+            '-Alpha'    => __('Alphabetical (Reversed)'),
+            'SortCol'   => __('Manually Sorted')
+        );
+    }
+
+    function getSortMode() {
+        return $this->sort_mode;
+    }
+
+    function getListOrderBy() {
+        switch ($this->getSortMode()) {
+            case 'Alpha':   return 'value';
+            case '-Alpha':  return '-value';
+            case 'SortCol': return 'sort';
+        }
+    }
+
+    function getName() {
+        return $this->get('name');
+    }
+
+    function getPluralName() {
+        if ($name = $this->get('name_plural'))
+            return $name;
+        else
+            return $this->get('name') . 's';
+    }
+
+    function getItemCount() {
+        return DynamicListItem::objects()->filter(array('list_id'=>$this->id))
+            ->count();
+    }
+
+    function getNumItems() {
+        return $this->getItemCount();
+    }
+
+    function getAllItems() {
+         return DynamicListItem::objects()->filter(
+                array('list_id'=>$this->get('id')))
+                ->order_by($this->getListOrderBy());
+    }
+
+    function getItems($limit=false, $offset=false) {
+        if (!$this->_items) {
+            $this->_items = DynamicListItem::objects()->filter(
+                array('list_id'=>$this->get('id'),
+                      'status__hasbit'=>DynamicListItem::ENABLED))
+                ->order_by($this->getListOrderBy());
+            if ($limit)
+                $this->_items->limit($limit);
+            if ($offset)
+                $this->_items->offset($offset);
+        }
+        return $this->_items;
+    }
+
+
+
+    function getItem($val) {
+
+        $criteria = array('list_id' => $this->getId());
+        if (is_int($val))
+            $criteria['id'] = $val;
+        else
+            $criteria['value'] = $val;
+
+         return DynamicListItem::lookup($criteria);
+    }
+
+    function addItem($vars, &$errors) {
+
+        $item = DynamicListItem::create(array(
+            'status' => 1,
+            'list_id' => $this->getId(),
+            'sort'  => $vars['sort'],
+            'value' => $vars['value'],
+            'extra' => $vars['abbrev']
+        ));
+
+        $item->save();
+
+        $this->_items = false;
+
+        return $item;
+    }
+
+    function getConfigurationForm($autocreate=false) {
+        if (!$this->_form) {
+            $this->_form = DynamicForm::lookup(array('type'=>'L'.$this->getId()));
+            if (!$this->_form
+                    && $autocreate
+                    && $this->createConfigurationForm())
+                return $this->getConfigurationForm(false);
+        }
+
+        return $this->_form;
+    }
+
+    function isDeleteable() {
+        return !$this->hasMask(static::MASK_DELETE);
+    }
+
+    function isEditable() {
+        return !$this->hasMask(static::MASK_EDIT);
+    }
+
+    function allowAdd() {
+        return !$this->hasMask(static::MASK_ADD);
+    }
+
+    function hasAbbrev() {
+        return !$this->hasMask(static::MASK_ABBREV);
+    }
+
+    protected function hasMask($mask) {
+        return 0 !== ($this->get('masks') & $mask);
+    }
+
+    protected function clearMask($mask) {
+        return $this->set('masks', $this->get('masks') & ~$mask);
+    }
+
+    protected function setFlag($mask) {
+        return $this->set('mask', $this->get('mask') | $mask);
+    }
+
+    private function createConfigurationForm() {
+
+        $form = DynamicForm::create(array(
+                    'type' => 'L'.$this->getId(),
+                    'title' => $this->getName() . ' Properties'
+        ));
+
+        return $form->save(true);
+    }
+
+    function getForm($autocreate=true) {
+        return $this->getConfigurationForm($autocreate);
+    }
+
+    function getConfiguration() {
+        return JsonDataParser::parse($this->_config->get('configuration'));
+    }
+
+    function update($vars, &$errors) {
+
+        $required = array();
+        if ($this->isEditable())
+            $required = array('name');
+
+        foreach (static::$fields as $f) {
+            if (in_array($f, $required) && !$vars[$f])
+                $errors[$f] = sprintf(__('%s is required'), mb_convert_case($f, MB_CASE_TITLE));
+            elseif (isset($vars[$f]))
+                $this->set($f, $vars[$f]);
+        }
+
+        if ($errors)
+            return false;
+
+        return $this->save(true);
+    }
+
+    function save($refetch=false) {
+        if (count($this->dirty))
+            $this->set('updated', new SqlFunction('NOW'));
+        if (isset($this->dirty['notes']))
+            $this->notes = Format::sanitize($this->notes);
+        return parent::save($refetch);
+    }
+
+    function delete() {
+        $fields = DynamicFormField::objects()->filter(array(
+            'type'=>'list-'.$this->id))->count();
+        if ($fields == 0)
+            return parent::delete();
+        else
+            // Refuse to delete lists that are in use by fields
+            return false;
+    }
+
+    private function createForm() {
+
+        $form = DynamicForm::create(array(
+                    'type' => 'L'.$this->getId(),
+                    'title' => $this->getName() . ' Properties'
+        ));
+
+        return $form->save(true);
+    }
+
+    static function add($vars, &$errors) {
+
+        $required = array('name');
+        $ht = array();
+        foreach (static::$fields as $f) {
+            if (in_array($f, $required) && !$vars[$f])
+                $errors[$f] = sprintf(__('%s is required'), mb_convert_case($f, MB_CASE_TITLE));
+            elseif(isset($vars[$f]))
+                $ht[$f] = $vars[$f];
+        }
+
+        if (!$ht || $errors)
+            return false;
+
+        // Create the list && form
+        if (!($list = self::create($ht))
+                || !$list->save(true)
+                || !$list->createConfigurationForm())
+            return false;
+
+        return $list;
+    }
+
+    static function create($ht=false, &$errors=array()) {
+        $inst = parent::create($ht);
+        $inst->set('created', new SqlFunction('NOW'));
+
+        if (isset($ht['properties'])) {
+            $inst->save();
+            $ht['properties']['type'] = 'L'.$inst->getId();
+            $form = DynamicForm::create($ht['properties']);
+            $form->save();
+        }
+
+        if (isset($ht['configuration'])) {
+            $inst->save();
+            $c = new Config('list.'.$inst->getId());
+            $c->set('configuration', JsonDataEncoder::encode($ht['configuration']));
+        }
+
+        if (isset($ht['items'])) {
+            $inst->save();
+            foreach ($ht['items'] as $i) {
+                $i['list_id'] = $inst->getId();
+                $item = DynamicListItem::create($i);
+                $item->save();
+            }
+        }
+
+        return $inst;
+    }
+
+    static function lookup($id) {
+
+        if (!($list = parent::lookup($id)))
+            return null;
+
+        if (($config = $list->getConfiguration())) {
+            if (($lh=$config['handler']) && class_exists($lh))
+                $list = new $lh($list);
+        }
+
+        return $list;
+    }
+
+    static function getSelections() {
+        $selections = array();
+        foreach (DynamicList::objects() as $list) {
+            $selections['list-'.$list->id] =
+                array($list->getPluralName(),
+                    SelectionField, $list->get('id'));
+        }
+        return $selections;
+    }
+
+}
+FormField::addFieldTypes(/* @trans */ 'Custom Lists', array('DynamicList', 'getSelections'));
+
+/**
+ * Represents a single item in a dynamic list
+ *
+ * Fields:
+ * value - (char * 255) Actual list item content
+ * extra - (char * 255) Other values that represent the same item in the
+ *      list, such as an abbreviation. In practice, should be a
+ *      space-separated list of tokens which should hit this list item in a
+ *      search
+ * sort - (int) If sorting by this field, represents the numeric sort order
+ *      that this item should come in the dropdown list
+ */
+class DynamicListItem extends VerySimpleModel implements CustomListItem {
+
+    static $meta = array(
+        'table' => LIST_ITEM_TABLE,
+        'pk' => array('id'),
+        'joins' => array(
+            'list' => array(
+                'null' => true,
+                'constraint' => array('list_id' => 'DynamicList.id'),
+            ),
+        ),
+    );
+
+    var $_config;
+    var $_form;
+
+    const ENABLED   = 0x0001;
+    const INTERNAL  = 0x0002;
+
+    protected function hasStatus($flag) {
+        return 0 !== ($this->get('status') & $flag);
+    }
+
+    protected function clearStatus($flag) {
+        return $this->set('status', $this->get('status') & ~$flag);
+    }
+
+    protected function setStatus($flag) {
+        return $this->set('status', $this->get('status') | $flag);
+    }
+
+    function isInternal() {
+        return  $this->hasStatus(self::INTERNAL);
+    }
+
+    function isEnableable() {
+        return true;
+    }
+
+    function isDisableable() {
+        return !$this->isInternal();
+    }
+
+    function isDeletable() {
+        return !$this->isInternal();
+    }
+
+    function isEnabled() {
+        return $this->hasStatus(self::ENABLED);
+    }
+
+    function enable() {
+        $this->setStatus(self::ENABLED);
+    }
+    function disable() {
+        $this->clearStatus(self::ENABLED);
+    }
+
+    function getId() {
+        return $this->get('id');
+    }
+
+    function getListId() {
+        return $this->get('list_id');
+    }
+
+    function getValue() {
+        return $this->get('value');
+    }
+
+    function getAbbrev() {
+        return $this->get('extra');
+    }
+
+    function getSortOrder() {
+        return $this->get('sort');
+    }
+
+    function getConfiguration() {
+        if (!$this->_config) {
+            $this->_config = $this->get('properties');
+            if (is_string($this->_config))
+                $this->_config = JsonDataParser::parse($this->_config);
+            elseif (!$this->_config)
+                $this->_config = array();
+        }
+        return $this->_config;
+    }
+
+    function setConfiguration(&$errors=array()) {
+        $config = array();
+        foreach ($this->getConfigurationForm($_POST)->getFields() as $field) {
+            $config[$field->get('id')] = $field->to_php($field->getClean());
+            $errors = array_merge($errors, $field->errors());
+        }
+        if (count($errors) === 0)
+            $this->set('properties', JsonDataEncoder::encode($config));
+
+        return count($errors) === 0;
+    }
+
+    function getConfigurationForm($source=null) {
+        if (!$this->_form) {
+            $config = $this->getConfiguration();
+            $this->_form = DynamicForm::lookup(
+                array('type'=>'L'.$this->get('list_id')))->getForm($source);
+            if (!$source && $config) {
+                $fields = $this->_form->getFields();
+                foreach ($fields as $f) {
+                    $name = $f->get('id');
+                    if (isset($config[$name]))
+                        $f->value = $f->to_php($config[$name]);
+                    else if ($f->get('default'))
+                        $f->value = $f->get('default');
+                }
+            }
+        }
+
+        return $this->_form;
+    }
+
+    function getForm() {
+        return $this->getConfigurationForm();
+    }
+
+    function getVar($name) {
+        $config = $this->getConfiguration();
+        $name = mb_strtolower($name);
+        foreach ($this->getConfigurationForm()->getFields() as $field) {
+            if (mb_strtolower($field->get('name')) == $name)
+                return $config[$field->get('id')];
+        }
+    }
+
+    function toString() {
+        return $this->get('value');
+    }
+
+    function __toString() {
+        return $this->toString();
+    }
+
+    function update($vars, &$errors=array()) {
+
+        if (!$vars['value']) {
+            $errors['value-'.$this->getId()] = __('Value required');
+            return false;
+        }
+
+        foreach (array(
+                    'sort' => 'sort',
+                    'value' => 'value',
+                    'abbrev' => 'extra') as $k => $v) {
+            if (isset($vars[$k]))
+                $this->set($v, $vars[$k]);
+        }
+
+        return $this->save();
+    }
+
+    function delete() {
+        # Don't really delete, just unset the list_id to un-associate it with
+        # the list
+        $this->set('list_id', null);
+        return $this->save();
+    }
+
+    static function create($ht=false, &$errors=array()) {
+
+        if (isset($ht['properties']) && is_array($ht['properties']))
+            $ht['properties'] = JsonDataEncoder::encode($ht['properties']);
+
+        $inst = parent::create($ht);
+        $inst->save(true);
+
+        // Auto-config properties if any
+        if ($ht['configuration'] && is_array($ht['configuration'])) {
+            $config = $inst->getConfiguration();
+            if (($form = $inst->getConfigurationForm())) {
+                foreach ($form->getFields() as $f) {
+                    if (!isset($ht['configuration'][$f->get('name')]))
+                        continue;
+
+                    $config[$f->get('id')] =
+                        $ht['configuration'][$f->get('name')];
+                }
+            }
+
+            $inst->set('properties', JsonDataEncoder::encode($config));
+        }
+
+        return $inst;
+    }
+}
+
+
+/*
+ * Ticket status List
+ *
+ */
+
+class TicketStatusList extends CustomListHandler {
+
+    // Fields of interest we need to store
+    static $config_fields = array('sort_mode', 'notes');
+
+    var $_items;
+    var $_form;
+
+    function getNumItems() {
+        return TicketStatus::objects()->count();
+    }
+
+    function getAllItems() {
+        if (!$this->_items)
+            $this->_items = TicketStatus::objects()->order_by($this->getListOrderBy());
+
+        return $this->_items;
+    }
+
+    function getItems($criteria = array()) {
+
+        // Default to only enabled items
+        if (!isset($criteria['enabled']))
+            $criteria['enabled'] = true;
+
+        $filters =  array();
+        if ($criteria['enabled'])
+            $filters['mode__hasbit'] = TicketStatus::ENABLED;
+        if ($criteria['states'] && is_array($criteria['states']))
+            $filters['state__in'] = $criteria['states'];
+        else
+            $filters['state__isnull'] = false;
+
+        $items = TicketStatus::objects();
+        if ($filters)
+            $items->filter($filters);
+        if ($criteria['limit'])
+            $items->limit($criteria['limit']);
+        if ($criteria['offset'])
+            $items->offset($criteria['offset']);
+
+        $items->order_by($this->getListOrderBy());
+
+        return $items;
+    }
+
+    function getItem($val) {
+
+        if (!is_int($val))
+            $val = array('name' => $val);
+
+         return TicketStatus::lookup($val, $this);
+    }
+
+    function addItem($vars, &$errors) {
+
+        $item = TicketStatus::create(array(
+            'mode' => 1,
+            'flags' => 0,
+            'sort'  => $vars['sort'],
+            'name' => $vars['value'],
+        ));
+        $item->save();
+
+        $this->_items = false;
+
+        return $item;
+    }
+
+    static function getStatuses($criteria=array()) {
+
+        $statuses = array();
+        if (($list = DynamicList::lookup(
+                        array('type' => 'ticket-status'))))
+            $statuses = $list->getItems($criteria);
+
+        return $statuses;
+    }
+
+    static function __load() {
+        require_once(INCLUDE_DIR.'class.i18n.php');
+
+        $i18n = new Internationalization();
+        $tpl = $i18n->getTemplate('list.yaml');
+        foreach ($tpl->getData() as $f) {
+            if ($f['type'] == 'ticket-status') {
+                $list = DynamicList::create($f);
+                $list->save();
+                break;
+            }
+        }
+
+        if (!$list || !($o=DynamicForm::objects()->filter(
+                        array('type'=>'L'.$list->getId()))))
+            return false;
+
+        // Create default statuses
+        if (($statuses = $i18n->getTemplate('ticket_status.yaml')->getData()))
+            foreach ($statuses as $status)
+                TicketStatus::__create($status);
+
+        return $o[0];
+    }
+}
+
+class TicketStatus  extends VerySimpleModel implements CustomListItem {
+
+    static $meta = array(
+        'table' => TICKET_STATUS_TABLE,
+        'ordering' => array('name'),
+        'pk' => array('id'),
+        'joins' => array(
+            'tickets' => array(
+                'reverse' => 'TicketModel.status',
+                )
+        )
+    );
+
+    var $_list;
+    var $_form;
+    var $_settings;
+    var $_properties;
+
+    const ENABLED   = 0x0001;
+    const INTERNAL  = 0x0002; // Forbid deletion or name and status change.
+
+
+
+    function __construct() {
+        call_user_func_array(array('parent', '__construct'), func_get_args());
+    }
+
+    protected function hasFlag($field, $flag) {
+        return 0 !== ($this->get($field) & $flag);
+    }
+
+    protected function clearFlag($field, $flag) {
+        return $this->set($field, $this->get($field) & ~$flag);
+    }
+
+    protected function setFlag($field, $flag) {
+        return $this->set($field, $this->get($field) | $flag);
+    }
+
+    protected function hasProperties() {
+        return ($this->get('properties'));
+    }
+
+    function getForm() {
+        if (!$this->_form && $this->_list) {
+            $this->_form = DynamicForm::lookup(
+                array('type'=>'L'.$this->_list->getId()));
+        }
+        return $this->_form;
+    }
+
+    function getExtraConfigOptions($source=null) {
+
+
+        $status_choices = array( 0 => __('System Default'));
+        if (($statuses=TicketStatusList::getStatuses(
+                        array( 'enabled' => true, 'states' =>
+                            array('open')))))
+            foreach ($statuses as $s)
+                $status_choices[$s->getId()] = $s->getName();
+
+
+        return array(
+            'allowreopen' => new BooleanField(array(
+                'label' =>__('Allow Reopen'),
+                'default' => isset($source['allowreopen'])
+                    ?  $source['allowreopen']: true,
+                'id' => 'allowreopen',
+                'name' => 'allowreopen',
+                'configuration' => array(
+                    'desc'=>__('Allow tickets on this status to be reopened by end users'),
+                ),
+                'visibility' => new VisibilityConstraint(
+                    new Q(array('state__eq'=>'closed')),
+                    VisibilityConstraint::HIDDEN
+                ),
+            )),
+            'reopenstatus' => new ChoiceField(array(
+                'label' => __('Reopen Status'),
+                'required' => false,
+                'default' => isset($source['reopenstatus'])
+                    ? $source['reopenstatus'] : 0,
+                'id' => 'reopenstatus',
+                'name' => 'reopenstatus',
+                'choices' => $status_choices,
+                'configuration' => array(
+                    'widget' => 'dropdown',
+                    'multiselect' =>false
+                ),
+                'visibility' => new VisibilityConstraint(
+                    new Q(array('allowreopen__eq'=> true)),
+                    VisibilityConstraint::HIDDEN
+                ),
+            ))
+        );
+    }
+
+    function getConfigurationForm($source=null) {
+
+        if (!($form = $this->getForm()))
+            return null;
+
+        $config = $this->getConfiguration();
+        $form = $form->getForm($source);
+        $fields = $form->getFields();
+        foreach ($fields as $k => $f) {
+            if ($f->get('name') == 'state' //TODO: check if editable.
+                    && ($extras=$this->getExtraConfigOptions($source))) {
+                foreach ($extras as $extra) {
+                    $extra->setForm($form);
+                    $fields->insert(++$k, $extra);
+                }
+                break;
+            }
+        }
+
+        if (!$source && $config) {
+            foreach ($fields as $f) {
+                $name = $f->get('id');
+                if (isset($config[$name]))
+                    $f->value = $f->to_php($config[$name]);
+                else if ($f->get('default'))
+                    $f->value = $f->get('default');
+            }
+        }
+
+        return $form;
+    }
+
+    function isEnabled() {
+        return $this->hasFlag('mode', self::ENABLED);
+    }
+
+    function isReopenable() {
+
+        if (strcasecmp($this->get('state'), 'closed'))
+            return true;
+
+        if (($c=$this->getConfiguration())
+                && $c['allowreopen']
+                && isset($c['reopenstatus']))
+            return true;
+
+        return false;
+    }
+
+    function getReopenStatus() {
+        global $cfg;
+
+        $status = null;
+        if ($this->isReopenable()
+                && ($c = $this->getConfiguration())
+                && isset($c['reopenstatus']))
+            $status = TicketStatus::lookup(
+                    $c['reopenstatus'] ?: $cfg->getDefaultTicketStatusId());
+
+        return ($status
+                && !strcasecmp($status->getState(), 'open'))
+            ?  $status : null;
+    }
+
+    function enable() {
+
+        // Ticket status without properties cannot be enabled!
+        if (!$this->isEnableable())
+            return false;
+
+        return $this->setFlag('mode', self::ENABLED);
+    }
+
+    function disable() {
+        return (!$this->isInternal()
+                && !$this->isDefault()
+                && $this->clearFlag('mode', self::ENABLED));
+    }
+
+    function isDefault() {
+        global $cfg;
+
+        return ($cfg
+                && $cfg->getDefaultTicketStatusId() == $this->getId());
+    }
+
+    function isEnableable() {
+        return ($this->getState());
+    }
+
+    function isDisableable() {
+        return !($this->isInternal() || $this->isDefault());
+    }
+
+    function isDeletable() {
+
+        return !($this->isInternal()
+                || $this->isDefault()
+                || $this->getNumTickets());
+    }
+
+    function isInternal() {
+        return ($this->isDefault()
+                || $this->hasFlag('mode', self::INTERNAL));
+    }
+
+
+    function getNumTickets() {
+        return $this->tickets->count();
+    }
+
+    function getId() {
+        return $this->get('id');
+    }
+
+    function getName() {
+        return $this->get('name');
+    }
+
+    function getState() {
+        return $this->get('state');
+    }
+
+    function getValue() {
+        return $this->getName();
+    }
+
+    function getAbbrev() {
+        return '';
+    }
+
+    function getSortOrder() {
+        return $this->get('sort');
+    }
+
+    private function getProperties() {
+
+        if (!isset($this->_properties)) {
+            $this->_properties = $this->get('properties');
+            if (is_string($this->_properties))
+                $this->_properties = JsonDataParser::parse($this->_properties);
+            elseif (!$this->_properties)
+                $this->_properties = array();
+        }
+
+        return $this->_properties;
+    }
+
+    function getConfiguration() {
+
+        if (!$this->_settings) {
+            $this->_settings = $this->getProperties();
+            if (!$this->_settings)
+                $this->_settings = array();
+
+            if ($this->getForm()) {
+                foreach ($this->getForm()->getFields() as $f)  {
+                    $name = mb_strtolower($f->get('name'));
+                    $id = $f->get('id');
+                    switch($name) {
+                        case 'flags':
+                            foreach (TicketFlagField::$_flags as $k => $v)
+                                if ($this->hasFlag('flags', $v['flag']))
+                                    $this->_settings[$id][$k] = $v['name'];
+                            break;
+                        case 'state':
+                            $this->_settings[$id][$this->get('state')] = $this->get('state');
+                            break;
+                        default:
+                            if (!$this->_settings[$id] && $this->_settings[$name])
+                                $this->_settings[$id] = $this->_settings[$name];
+                    }
+                }
+            }
+        }
+
+        return $this->_settings;
+    }
+
+    function setConfiguration(&$errors=array()) {
+        $properties = array();
+        foreach ($this->getConfigurationForm($_POST)->getFields() as $f) {
+            if ($this->isInternal() //Item is internal.
+                    && !$f->isEditable())
+                continue;
+            $val = $f->getClean();
+            $errors = array_merge($errors, $f->errors());
+            if ($f->errors()) continue;
+            $name = mb_strtolower($f->get('name'));
+            switch ($name) {
+                case 'flags':
+                    if ($val && is_array($val)) {
+                        $flags = 0;
+                        foreach ($val as $k => $v) {
+                            if (isset(TicketFlagField::$_flags[$k]))
+                                $flags += TicketFlagField::$_flags[$k]['flag'];
+                            elseif (!$f->errors())
+                                $f->addError(__('Unknown or invalid flag'), $name);
+                        }
+                        $this->set('flags', $flags);
+                    } elseif ($val && !$f->errors()) {
+                        $f->addError(__('Unknown or invalid flag format'), $name);
+                    }
+                    break;
+                case 'state':
+                    if ($val)
+                        $this->set('state', $val);
+                    else
+                        $f->addError(__('Unknown or invalid state'), $name);
+                    break;
+                default: //Custom properties the user might add.
+                    $properties[$f->get('id')] = $f->to_php($val);
+            }
+            // Add field specific validation errors (warnings)
+            $errors = array_merge($errors, $f->errors());
+        }
+
+        if (count($errors) === 0) {
+            if ($properties && is_array($properties))
+                $properties = JsonDataEncoder::encode($properties);
+
+            $this->set('properties', $properties);
+            $this->save(true);
+        }
+
+        return count($errors) === 0;
+    }
+
+    function update($vars, &$errors) {
+
+        $fields = array('value' => 'name', 'sort' => 'sort');
+        foreach($fields as $k => $v) {
+            if (isset($vars[$k]))
+                $this->set($v, $vars[$k]);
+        }
+
+        return $this->save(true);
+    }
+
+    function delete() {
+
+        // Statuses with tickets are not deletable
+        if (!$this->isDeletable())
+            return false;
+
+        return parent::delete();
+    }
+
+    function __toString() {
+        return __($this->getName());
+    }
+
+    static function create($ht) {
+
+        if (!isset($ht['mode']))
+            $ht['mode'] = 1;
+
+        $ht['created'] = new SqlFunction('NOW');
+
+        return  parent::create($ht);
+    }
+
+    static function lookup($var, $list= false) {
+
+        if (!($item = parent::lookup($var)))
+            return null;
+
+        $item->_list = $list;
+
+        return $item;
+    }
+
+
+    static function __create($ht, &$error=false) {
+        global $ost;
+
+        $ht['properties'] = JsonDataEncoder::encode($ht['properties']);
+        if (($status = TicketStatus::create($ht)))
+            $status->save(true);
+
+        return $status;
+    }
+
+    static function status_options() {
+        include(STAFFINC_DIR . 'templates/status-options.tmpl.php');
+    }
+}
+
+TicketStatus::_inspect();
+?>
diff --git a/include/class.mailer.php b/include/class.mailer.php
index 0848a559093a562a419b3951e56026ccb5938eb8..a3093c203a14eac9f8e7a2082830f715352b2128 100644
--- a/include/class.mailer.php
+++ b/include/class.mailer.php
@@ -279,7 +279,7 @@ class Mailer {
             // Force reconnect on next ->send()
             unset($smtp_connections[$key]);
 
-            $alert=sprintf("Unable to email via SMTP:%s:%d [%s]\n\n%s\n",
+            $alert=sprintf(__("Unable to email via SMTP:%1\$s:%2\$d [%3\$s]\n\n%4\$s\n"),
                     $smtp['host'], $smtp['port'], $smtp['username'], $result->getMessage());
             $this->logError($alert);
         }
@@ -293,7 +293,7 @@ class Mailer {
     function logError($error) {
         global $ost;
         //NOTE: Admin alert override - don't email when having email trouble!
-        $ost->logError('Mailer Error', $error, false);
+        $ost->logError(__('Mailer Error'), $error, false);
     }
 
     /******* Static functions ************/
diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php
index 0958e8be1015065fbac6b369c474a1bb251661b8..4e6546549f44d7ad520670bb01af8e946534aaea 100644
--- a/include/class.mailfetch.php
+++ b/include/class.mailfetch.php
@@ -201,7 +201,6 @@ class MailFetcher {
             $text=imap_qprint($text);
             break;
         }
-
         return $text;
     }
 
@@ -317,7 +316,7 @@ class MailFetcher {
                     if ($source == 'delivered-to') continue;
 
                     $header['recipients'][] = array(
-                            'source' => "Email ($source)",
+                            'source' => sprintf(_S("Email (%s)"),$source),
                             'name' => $this->mime_decode(@$addr->personal),
                             'email' => strtolower($addr->mailbox).'@'.$addr->host);
                 } elseif(!$header['emailId']) {
@@ -463,7 +462,7 @@ class MailFetcher {
             // send images without a filename. For such a case, generate a
             // random filename for the image
             if (!$filename && $content_id && $part->type == 5) {
-                $filename = 'image-'.Misc::randCode(4).'.'.strtolower($part->subtype);
+                $filename = _S('image').'-'.Misc::randCode(4).'.'.strtolower($part->subtype);
             }
 
             if($filename) {
@@ -603,7 +602,8 @@ class MailFetcher {
 	    //Is the email address banned?
         if($mailinfo['email'] && TicketFilter::isBanned($mailinfo['email'])) {
 	        //We need to let admin know...
-            $ost->logWarning('Ticket denied', 'Banned email - '.$mailinfo['email'], false);
+            $ost->logWarning(_S('Ticket denied'),
+                sprintf(_S('Banned email — %s'),$mailinfo['email']), false);
 	        return true; //Report success (moved or delete)
         }
 
@@ -665,8 +665,14 @@ class MailFetcher {
         $errors=array();
         $seen = false;
 
+        // Use the settings on the thread entry on the ticket details
+        // form to validate the attachments in the email
+        $tform = TicketForm::objects()->one()->getForm();
+        $messageField = $tform->getField('message');
+        $fileField = $messageField->getWidget()->getAttachments();
+
         // Fetch attachments if any.
-        if($ost->getConfig()->allowEmailAttachments()) {
+        if ($messageField->isAttachmentsEnabled()) {
             // Include TNEF attachments in the attachments list
             if ($this->tnef) {
                 foreach ($this->tnef->attachments as $at) {
@@ -680,14 +686,11 @@ class MailFetcher {
                 }
             }
             $vars['attachments'] = array();
-            foreach($attachments as $a ) {
+
+            foreach ($attachments as $a) {
                 $file = array('name' => $a['name'], 'type' => $a['type']);
 
-                //Check the file  type
-                if(!$ost->isFileTypeAllowed($file)) {
-                    $file['error'] = 'Invalid file type (ext) for '.Format::htmlchars($file['name']);
-                }
-                elseif (@$a['data'] instanceof TnefAttachment) {
+                if (@$a['data'] instanceof TnefAttachment) {
                     $file['data'] = $a['data']->getData();
                 }
                 else {
@@ -700,6 +703,15 @@ class MailFetcher {
                 }
                 // Include the Content-Id if specified (for inline images)
                 $file['cid'] = isset($a['cid']) ? $a['cid'] : false;
+
+                // Validate and save immediately
+                try {
+                    $file['id'] = $fileField->uploadAttachment($file);
+                }
+                catch (FileUploadError $ex) {
+                    $file['error'] = $file['name'] . ': ' . $ex->getMessage();
+                }
+
                 $vars['attachments'][] = $file;
             }
         }
@@ -709,6 +721,10 @@ class MailFetcher {
 
         $seen = false;
         if (($thread = ThreadEntry::lookupByEmailHeaders($vars, $seen))
+                && ($t=$thread->getTicket())
+                && ($vars['staffId']
+                    || !$t->isClosed()
+                    || $t->isReopenable())
                 && ($message = $thread->postEmail($vars))) {
             if (!$message instanceof ThreadEntry)
                 // Email has been processed previously
@@ -730,8 +746,8 @@ class MailFetcher {
 
             // Log an error to the system logs
             $mailbox = Email::lookup($vars['emailId']);
-            $ost->logError('Mail Processing Exception', sprintf(
-                "Mailbox: %s\nError(s): %s",
+            $ost->logError(_S('Mail Processing Exception'), sprintf(
+                _S("Mailbox: %s | Error(s): %s"),
                 $mailbox->getEmail(),
                 print_r($errors, true)
             ), false);
@@ -755,7 +771,6 @@ class MailFetcher {
 
     function fetchEmails() {
 
-
         if(!$this->connect())
             return false;
 
@@ -785,7 +800,7 @@ class MailFetcher {
 
         //Warn on excessive errors
         if($errors>$msgs) {
-            $warn=sprintf('Excessive errors processing emails for %s/%s. Please manually check the inbox.',
+            $warn=sprintf(_S('Excessive errors processing emails for %1$s/%2$s. Please manually check the inbox.'),
                     $this->getHost(), $this->getUsername());
             $this->log($warn);
         }
@@ -797,7 +812,7 @@ class MailFetcher {
 
     function log($error) {
         global $ost;
-        $ost->logWarning('Mail Fetcher', $error);
+        $ost->logWarning(_S('Mail Fetcher'), $error);
     }
 
     /*
@@ -814,8 +829,8 @@ class MailFetcher {
         //We require imap ext to fetch emails via IMAP/POP3
         //We check here just in case the extension gets disabled post email config...
         if(!function_exists('imap_open')) {
-            $msg='osTicket requires PHP IMAP extension enabled for IMAP/POP3 email fetch to work!';
-            $ost->logWarning('Mail Fetch Error', $msg);
+            $msg=_S('osTicket requires PHP IMAP extension enabled for IMAP/POP3 email fetch to work!');
+            $ost->logWarning(_S('Mail Fetch Error'), $msg);
             return;
         }
 
@@ -853,13 +868,13 @@ class MailFetcher {
                 db_query('UPDATE '.EMAIL_TABLE.' SET mail_errors=mail_errors+1, mail_lasterror=NOW() WHERE email_id='.db_input($emailId));
                 if (++$errors>=$MAXERRORS) {
                     //We've reached the MAX consecutive errors...will attempt logins at delayed intervals
-                    $msg="\nosTicket is having trouble fetching emails from the following mail account: \n".
-                        "\nUser: ".$fetcher->getUsername().
-                        "\nHost: ".$fetcher->getHost().
-                        "\nError: ".$fetcher->getLastError().
-                        "\n\n ".$errors.' consecutive errors. Maximum of '.$MAXERRORS. ' allowed'.
-                        "\n\n This could be connection issues related to the mail server. Next delayed login attempt in approx. $TIMEOUT minutes";
-                    $ost->alertAdmin('Mail Fetch Failure Alert', $msg, true);
+                    $msg="\n"._S('osTicket is having trouble fetching emails from the following mail account').": \n".
+                        "\n"._S('User').": ".$fetcher->getUsername().
+                        "\n"._S('Host').": ".$fetcher->getHost().
+                        "\n"._S('Error').": ".$fetcher->getLastError().
+                        "\n\n ".sprintf(_S('%1$d consecutive errors. Maximum of %2$d allowed'), $errors, $MAXERRORS).
+                        "\n\n ".sprintf(_S('This could be connection issues related to the mail server. Next delayed login attempt in aprox. %d minutes'),$TIMEOUT);
+                    $ost->alertAdmin(_S('Mail Fetch Failure Alert'), $msg, true);
                 }
             }
         } //end while.
diff --git a/include/class.mailparse.php b/include/class.mailparse.php
index fda03365826ce8b6a124e4bb1a5bc9d56bfc6865..4038f1226fa53b3a038bbea912ada03e3ceaf108 100644
--- a/include/class.mailparse.php
+++ b/include/class.mailparse.php
@@ -472,7 +472,7 @@ class Mail_Parse {
     }
 
     static function parsePriority($header=null){
-    	
+
     	if (! $header)
     		return 0;
     	// Test for normal "X-Priority: INT" style header & stringy version.
@@ -614,7 +614,7 @@ class EmailDataParser {
                     if ($source == 'delivered-to') continue;
 
                     $data['recipients'][] = array(
-                        'source' => "Email ($source)",
+                        'source' => sprintf(_S("Email (%s)"), $source),
                         'name' => trim(@$addr->personal, '"'),
                         'email' => strtolower($addr->mailbox).'@'.$addr->host);
                 } elseif(!$data['emailId']) {
@@ -678,8 +678,7 @@ class EmailDataParser {
                 $data['reply-to-name'] = trim($replyto->personal, " \t\n\r\0\x0B\x22");
         }
 
-        if($cfg && $cfg->allowEmailAttachments())
-            $data['attachments'] = $parser->getAttachments();
+        $data['attachments'] = $parser->getAttachments();
 
         return $data;
     }
diff --git a/include/class.misc.php b/include/class.misc.php
index 80c2b4b2408db9e8a93513cded9555e202f15e20..84871c5397e2311cc344e2b636f2fa0bbd006d9f 100644
--- a/include/class.misc.php
+++ b/include/class.misc.php
@@ -13,6 +13,7 @@
 
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
+
 class Misc {
 
 	function randCode($count=8, $chars=false) {
@@ -125,13 +126,14 @@ class Misc {
 
         ob_start();
         echo sprintf('<select name="%s" id="%s">',$name,$name);
-        echo '<option value="" selected>Time</option>';
+        echo '<option value="" selected>'.__('Time').'</option>';
+        $format = $cfg->getTimeFormat();
         for($i=23; $i>=0; $i--) {
             for($minute=45; $minute>=0; $minute-=15) {
                 $sel=($hr==$i && $min==$minute)?'selected="selected"':'';
                 $_minute=str_pad($minute, 2, '0',STR_PAD_LEFT);
                 $_hour=str_pad($i, 2, '0',STR_PAD_LEFT);
-                $disp = gmdate($cfg->getTimeFormat(), $i*3600 + $minute*60);
+                $disp = gmdate($format, $i*3600 + $minute*60);
                 echo sprintf('<option value="%s:%s" %s>%s</option>',$_hour,$_minute,$sel,$disp);
             }
         }
diff --git a/include/class.nav.php b/include/class.nav.php
index 20246ad4ba0df9e0b7895c6d41cef932c04ecc65..bc4956f084b4b810b17d242637f38bcc365bbee8 100644
--- a/include/class.nav.php
+++ b/include/class.nav.php
@@ -16,11 +16,9 @@
 require_once(INCLUDE_DIR.'class.app.php');
 
 class StaffNav {
-    var $tabs=array();
-    var $submenus=array();
 
     var $activetab;
-    var $activemenu;
+    var $activeMenu;
     var $panel;
 
     var $staff;
@@ -28,8 +26,21 @@ class StaffNav {
     function StaffNav($staff, $panel='staff'){
         $this->staff=$staff;
         $this->panel=strtolower($panel);
-        $this->tabs=$this->getTabs();
-        $this->submenus=$this->getSubMenus();
+    }
+
+    function __get($what) {
+        // Lazily initialize the tabbing system
+        switch($what) {
+        case 'tabs':
+            $this->tabs=$this->getTabs();
+            break;
+        case 'submenus':
+            $this->submenus=$this->getSubMenus();
+            break;
+        default:
+            throw new Exception($what . ': No such attribute');
+        }
+        return $this->{$what};
     }
 
     function getPanel(){
@@ -91,6 +102,8 @@ class StaffNav {
 
     function addSubMenu($item,$active=false){
 
+        // Triger lazy loading if submenus haven't been initialized
+        isset($this->submenus[$this->getPanel().'.'.$this->activetab]);
         $this->submenus[$this->getPanel().'.'.$this->activetab][]=$item;
         if($active)
             $this->activeMenu=sizeof($this->submenus[$this->getPanel().'.'.$this->activetab]);
@@ -98,15 +111,14 @@ class StaffNav {
 
 
     function getTabs(){
-
         if(!$this->tabs) {
             $this->tabs=array();
-            $this->tabs['dashboard'] = array('desc'=>'Dashboard','href'=>'dashboard.php','title'=>'Staff Dashboard');
-            $this->tabs['users'] = array('desc' => 'Users', 'href' => 'users.php', 'title' => 'User Directory');
-            $this->tabs['tickets'] = array('desc'=>'Tickets','href'=>'tickets.php','title'=>'Ticket Queue');
-            $this->tabs['kbase'] = array('desc'=>'Knowledgebase','href'=>'kb.php','title'=>'Knowledgebase');
+            $this->tabs['dashboard'] = array('desc'=>__('Dashboard'),'href'=>'dashboard.php','title'=>__('Agent Dashboard'));
+            $this->tabs['users'] = array('desc' => __('Users'), 'href' => 'users.php', 'title' => __('User Directory'));
+            $this->tabs['tickets'] = array('desc'=>__('Tickets'),'href'=>'tickets.php','title'=>__('Ticket Queue'));
+            $this->tabs['kbase'] = array('desc'=>__('Knowledgebase'),'href'=>'kb.php','title'=>__('Knowledgebase'));
             if (count($this->getRegisteredApps()))
-                $this->tabs['apps']=array('desc'=>'Applications','href'=>'apps.php','title'=>'Applications');
+                $this->tabs['apps']=array('desc'=>__('Applications'),'href'=>'apps.php','title'=>__('Applications'));
         }
 
         return $this->tabs;
@@ -120,17 +132,17 @@ class StaffNav {
             $subnav=array();
             switch(strtolower($k)){
                 case 'tickets':
-                    $subnav[]=array('desc'=>'Tickets','href'=>'tickets.php','iconclass'=>'Ticket', 'droponly'=>true);
+                    $subnav[]=array('desc'=>__('Tickets'),'href'=>'tickets.php','iconclass'=>'Ticket', 'droponly'=>true);
                     if($staff) {
                         if(($assigned=$staff->getNumAssignedTickets()))
-                            $subnav[]=array('desc'=>"My&nbsp;Tickets ($assigned)",
+                            $subnav[]=array('desc'=>__('My&nbsp;Tickets')." ($assigned)",
                                             'href'=>'tickets.php?status=assigned',
                                             'iconclass'=>'assignedTickets',
                                             'droponly'=>true);
 
                         if($staff->canCreateTickets())
-                            $subnav[]=array('desc'=>'New&nbsp;Ticket',
-                                            'title' => 'Open New Ticket',
+                            $subnav[]=array('desc'=>__('New Ticket'),
+                                            'title' => __('Open a New Ticket'),
                                             'href'=>'tickets.php?a=open',
                                             'iconclass'=>'newTicket',
                                             'id' => 'new-ticket',
@@ -138,21 +150,21 @@ class StaffNav {
                     }
                     break;
                 case 'dashboard':
-                    $subnav[]=array('desc'=>'Dashboard','href'=>'dashboard.php','iconclass'=>'logs');
-                    $subnav[]=array('desc'=>'Staff&nbsp;Directory','href'=>'directory.php','iconclass'=>'teams');
-                    $subnav[]=array('desc'=>'My&nbsp;Profile','href'=>'profile.php','iconclass'=>'users');
+                    $subnav[]=array('desc'=>__('Dashboard'),'href'=>'dashboard.php','iconclass'=>'logs');
+                    $subnav[]=array('desc'=>__('Agent Directory'),'href'=>'directory.php','iconclass'=>'teams');
+                    $subnav[]=array('desc'=>__('My Profile'),'href'=>'profile.php','iconclass'=>'users');
                     break;
                 case 'users':
-                    $subnav[] = array('desc' => 'User&nbsp;Directory', 'href' => 'users.php', 'iconclass' => 'teams');
-                    $subnav[] = array('desc' => 'Organizations', 'href' => 'orgs.php', 'iconclass' => 'departments');
+                    $subnav[] = array('desc' => __('User Directory'), 'href' => 'users.php', 'iconclass' => 'teams');
+                    $subnav[] = array('desc' => __('Organizations'), 'href' => 'orgs.php', 'iconclass' => 'departments');
                     break;
                 case 'kbase':
-                    $subnav[]=array('desc'=>'FAQs','href'=>'kb.php', 'urls'=>array('faq.php'), 'iconclass'=>'kb');
+                    $subnav[]=array('desc'=>__('FAQs'),'href'=>'kb.php', 'urls'=>array('faq.php'), 'iconclass'=>'kb');
                     if($staff) {
                         if($staff->canManageFAQ())
-                            $subnav[]=array('desc'=>'Categories','href'=>'categories.php','iconclass'=>'faq-categories');
+                            $subnav[]=array('desc'=>__('Categories'),'href'=>'categories.php','iconclass'=>'faq-categories');
                         if($staff->canManageCannedResponses())
-                            $subnav[]=array('desc'=>'Canned&nbsp;Responses','href'=>'canned.php','iconclass'=>'canned');
+                            $subnav[]=array('desc'=>__('Canned Responses'),'href'=>'canned.php','iconclass'=>'canned');
                     }
                    break;
                 case 'apps':
@@ -190,17 +202,16 @@ class AdminNav extends StaffNav{
 
     function getTabs(){
 
-
         if(!$this->tabs){
 
             $tabs=array();
-            $tabs['dashboard']=array('desc'=>'Dashboard','href'=>'logs.php','title'=>'Admin Dashboard');
-            $tabs['settings']=array('desc'=>'Settings','href'=>'settings.php','title'=>'System Settings');
-            $tabs['manage']=array('desc'=>'Manage','href'=>'helptopics.php','title'=>'Manage Options');
-            $tabs['emails']=array('desc'=>'Emails','href'=>'emails.php','title'=>'Email Settings');
-            $tabs['staff']=array('desc'=>'Staff','href'=>'staff.php','title'=>'Manage Staff');
+            $tabs['dashboard']=array('desc'=>__('Dashboard'),'href'=>'logs.php','title'=>__('Admin Dashboard'));
+            $tabs['settings']=array('desc'=>__('Settings'),'href'=>'settings.php','title'=>__('System Settings'));
+            $tabs['manage']=array('desc'=>__('Manage'),'href'=>'helptopics.php','title'=>__('Manage Options'));
+            $tabs['emails']=array('desc'=>__('Emails'),'href'=>'emails.php','title'=>__('Email Settings'));
+            $tabs['staff']=array('desc'=>__('Agents'),'href'=>'staff.php','title'=>__('Manage Agents'));
             if (count($this->getRegisteredApps()))
-                $tabs['apps']=array('desc'=>'Applications','href'=>'apps.php','title'=>'Applications');
+                $tabs['apps']=array('desc'=>__('Applications'),'href'=>'apps.php','title'=>__('Applications'));
             $this->tabs=$tabs;
         }
 
@@ -214,42 +225,42 @@ class AdminNav extends StaffNav{
             $subnav=array();
             switch(strtolower($k)){
                 case 'dashboard':
-                    $subnav[]=array('desc'=>'System&nbsp;Logs','href'=>'logs.php','iconclass'=>'logs');
-                    $subnav[]=array('desc'=>'Information','href'=>'system.php','iconclass'=>'preferences');
+                    $subnav[]=array('desc'=>__('System Logs'),'href'=>'logs.php','iconclass'=>'logs');
+                    $subnav[]=array('desc'=>__('Information'),'href'=>'system.php','iconclass'=>'preferences');
                     break;
                 case 'settings':
-                    $subnav[]=array('desc'=>'Company','href'=>'settings.php?t=pages','iconclass'=>'pages');
-                    $subnav[]=array('desc'=>'System','href'=>'settings.php?t=system','iconclass'=>'preferences');
-                    $subnav[]=array('desc'=>'Tickets','href'=>'settings.php?t=tickets','iconclass'=>'ticket-settings');
-                    $subnav[]=array('desc'=>'Emails','href'=>'settings.php?t=emails','iconclass'=>'email-settings');
-                    $subnav[]=array('desc'=>'Access','href'=>'settings.php?t=access','iconclass'=>'users');
-                    $subnav[]=array('desc'=>'Knowledgebase','href'=>'settings.php?t=kb','iconclass'=>'kb-settings');
-                    $subnav[]=array('desc'=>'Autoresponder','href'=>'settings.php?t=autoresp','iconclass'=>'email-autoresponders');
-                    $subnav[]=array('desc'=>'Alerts&nbsp;&amp;&nbsp;Notices','href'=>'settings.php?t=alerts','iconclass'=>'alert-settings');
+                    $subnav[]=array('desc'=>__('Company'),'href'=>'settings.php?t=pages','iconclass'=>'pages');
+                    $subnav[]=array('desc'=>__('System'),'href'=>'settings.php?t=system','iconclass'=>'preferences');
+                    $subnav[]=array('desc'=>__('Tickets'),'href'=>'settings.php?t=tickets','iconclass'=>'ticket-settings');
+                    $subnav[]=array('desc'=>__('Emails'),'href'=>'settings.php?t=emails','iconclass'=>'email-settings');
+                    $subnav[]=array('desc'=>__('Access'),'href'=>'settings.php?t=access','iconclass'=>'users');
+                    $subnav[]=array('desc'=>__('Knowledgebase'),'href'=>'settings.php?t=kb','iconclass'=>'kb-settings');
+                    $subnav[]=array('desc'=>__('Autoresponder'),'href'=>'settings.php?t=autoresp','iconclass'=>'email-autoresponders');
+                    $subnav[]=array('desc'=>__('Alerts and Notices'),'href'=>'settings.php?t=alerts','iconclass'=>'alert-settings');
                     break;
                 case 'manage':
-                    $subnav[]=array('desc'=>'Help&nbsp;Topics','href'=>'helptopics.php','iconclass'=>'helpTopics');
-                    $subnav[]=array('desc'=>'Ticket&nbsp;Filters','href'=>'filters.php',
-                                        'title'=>'Ticket&nbsp;Filters','iconclass'=>'ticketFilters');
-                    $subnav[]=array('desc'=>'SLA&nbsp;Plans','href'=>'slas.php','iconclass'=>'sla');
-                    $subnav[]=array('desc'=>'API&nbsp;Keys','href'=>'apikeys.php','iconclass'=>'api');
-                    $subnav[]=array('desc'=>'Pages', 'href'=>'pages.php','title'=>'Pages','iconclass'=>'pages');
-                    $subnav[]=array('desc'=>'Forms','href'=>'forms.php','iconclass'=>'forms');
-                    $subnav[]=array('desc'=>'Lists','href'=>'lists.php','iconclass'=>'lists');
-                    $subnav[]=array('desc'=>'Plugins','href'=>'plugins.php','iconclass'=>'api');
+                    $subnav[]=array('desc'=>__('Help Topics'),'href'=>'helptopics.php','iconclass'=>'helpTopics');
+                    $subnav[]=array('desc'=>__('Ticket Filters'),'href'=>'filters.php',
+                                        'title'=>__('Ticket Filters'),'iconclass'=>'ticketFilters');
+                    $subnav[]=array('desc'=>__('SLA Plans'),'href'=>'slas.php','iconclass'=>'sla');
+                    $subnav[]=array('desc'=>__('API Keys'),'href'=>'apikeys.php','iconclass'=>'api');
+                    $subnav[]=array('desc'=>__('Pages'), 'href'=>'pages.php','title'=>'Pages','iconclass'=>'pages');
+                    $subnav[]=array('desc'=>__('Forms'),'href'=>'forms.php','iconclass'=>'forms');
+                    $subnav[]=array('desc'=>__('Lists'),'href'=>'lists.php','iconclass'=>'lists');
+                    $subnav[]=array('desc'=>__('Plugins'),'href'=>'plugins.php','iconclass'=>'api');
                     break;
                 case 'emails':
-                    $subnav[]=array('desc'=>'Emails','href'=>'emails.php', 'title'=>'Email Addresses', 'iconclass'=>'emailSettings');
-                    $subnav[]=array('desc'=>'Banlist','href'=>'banlist.php',
-                                        'title'=>'Banned&nbsp;Emails','iconclass'=>'emailDiagnostic');
-                    $subnav[]=array('desc'=>'Templates','href'=>'templates.php','title'=>'Email Templates','iconclass'=>'emailTemplates');
-                    $subnav[]=array('desc'=>'Diagnostic','href'=>'emailtest.php', 'title'=>'Email Diagnostic', 'iconclass'=>'emailDiagnostic');
+                    $subnav[]=array('desc'=>__('Emails'),'href'=>'emails.php', 'title'=>__('Email Addresses'), 'iconclass'=>'emailSettings');
+                    $subnav[]=array('desc'=>__('Banlist'),'href'=>'banlist.php',
+                                        'title'=>__('Banned Emails'),'iconclass'=>'emailDiagnostic');
+                    $subnav[]=array('desc'=>__('Templates'),'href'=>'templates.php','title'=>__('Email Templates'),'iconclass'=>'emailTemplates');
+                    $subnav[]=array('desc'=>__('Diagnostic'),'href'=>'emailtest.php', 'title'=>__('Email Diagnostic'), 'iconclass'=>'emailDiagnostic');
                     break;
                 case 'staff':
-                    $subnav[]=array('desc'=>'Staff&nbsp;Members','href'=>'staff.php','iconclass'=>'users');
-                    $subnav[]=array('desc'=>'Teams','href'=>'teams.php','iconclass'=>'teams');
-                    $subnav[]=array('desc'=>'Groups','href'=>'groups.php','iconclass'=>'groups');
-                    $subnav[]=array('desc'=>'Departments','href'=>'departments.php','iconclass'=>'departments');
+                    $subnav[]=array('desc'=>__('Agents'),'href'=>'staff.php','iconclass'=>'users');
+                    $subnav[]=array('desc'=>__('Teams'),'href'=>'teams.php','iconclass'=>'teams');
+                    $subnav[]=array('desc'=>__('Groups'),'href'=>'groups.php','iconclass'=>'groups');
+                    $subnav[]=array('desc'=>__('Departments'),'href'=>'departments.php','iconclass'=>'departments');
                     break;
                 case 'apps':
                     foreach ($this->getRegisteredApps() as $app)
@@ -306,9 +317,9 @@ class UserNav {
 
             $navs = array();
             $user = $this->user;
-            $navs['home']=array('desc'=>'Support&nbsp;Center&nbsp;Home','href'=>'index.php','title'=>'');
+            $navs['home']=array('desc'=>__('Support Center Home'),'href'=>'index.php','title'=>'');
             if($cfg && $cfg->isKnowledgebaseEnabled())
-                $navs['kb']=array('desc'=>'Knowledgebase','href'=>'kb/index.php','title'=>'');
+                $navs['kb']=array('desc'=>__('Knowledgebase'),'href'=>'kb/index.php','title'=>'');
 
             // Show the "Open New Ticket" link unless BOTH client
             // registration is disabled and client login is required for new
@@ -316,19 +327,19 @@ class UserNav {
             // possible for web clients.
             if ($cfg->getClientRegistrationMode() != 'disabled'
                     || !$cfg->isClientLoginRequired())
-                $navs['new']=array('desc'=>'Open&nbsp;New&nbsp;Ticket','href'=>'open.php','title'=>'');
+                $navs['new']=array('desc'=>__('Open a New Ticket'),'href'=>'open.php','title'=>'');
             if($user && $user->isValid()) {
                 if(!$user->isGuest()) {
-                    $navs['tickets']=array('desc'=>sprintf('Tickets&nbsp;(%d)',$user->getNumTickets()),
+                    $navs['tickets']=array('desc'=>sprintf(__('Tickets (%d)'),$user->getNumTickets()),
                                            'href'=>'tickets.php',
-                                            'title'=>'Show all tickets');
+                                            'title'=>__('Show all tickets'));
                 } else {
-                    $navs['tickets']=array('desc'=>'View&nbsp;Ticket&nbsp;Thread',
+                    $navs['tickets']=array('desc'=>__('View Ticket Thread'),
                                            'href'=>sprintf('tickets.php?id=%d',$user->getTicketId()),
-                                           'title'=>'View ticket status');
+                                           'title'=>__('View ticket status'));
                 }
             } else {
-                $navs['status']=array('desc'=>'Check Ticket Status','href'=>'view.php','title'=>'');
+                $navs['status']=array('desc'=>__('Check Ticket Status'),'href'=>'view.php','title'=>'');
             }
             $this->navs=$navs;
         }
diff --git a/include/class.note.php b/include/class.note.php
index 4b0d36303335900a3c01f7f7c98889f41d05f2da..d16112d4ee2926b8a01b1d94cfd6d49e23a229c7 100644
--- a/include/class.note.php
+++ b/include/class.note.php
@@ -27,8 +27,8 @@ class QuickNoteModel extends VerySimpleModel {
 class QuickNote extends QuickNoteModel {
 
     static $types = array(
-        'U' => 'User',
-        'O' => 'Organization',
+        'U' => /* @trans */ 'User',
+        'O' => /* @trans */ 'Organization',
     );
     var $_staff;
 
@@ -62,7 +62,10 @@ class QuickNote extends QuickNoteModel {
     }
 
     function getIconTitle() {
-        return sprintf("%s Note", static::$types[$this->ext_id[0]]);
+        return sprintf(__(
+            // `%s` will be the type of note (`user` or `orgnaization`)
+            "%s Note"),
+            __(static::$types[$this->ext_id[0]]));
     }
 
     static function forUser($user, $org=false) {
diff --git a/include/class.organization.php b/include/class.organization.php
index 462b25df273ecbc4e26b22af1d9d7f22ab85d3f4..785b6d36b489416b99c164efcb6f20865cc6f250 100644
--- a/include/class.organization.php
+++ b/include/class.organization.php
@@ -116,17 +116,17 @@ class Organization extends OrganizationModel {
         return $of;
     }
 
-    function getDynamicData() {
+    function getDynamicData($create=true) {
         if (!isset($this->_entries)) {
             $this->_entries = DynamicFormEntry::forOrganization($this->id)->all();
-            if (!$this->_entries) {
+            if (!$this->_entries && $create) {
                 $g = OrganizationForm::getInstance($this->id, true);
                 $g->save();
                 $this->_entries[] = $g;
             }
         }
 
-        return $this->_entries;
+        return $this->_entries ?: array();
     }
 
     function getForms($data=null) {
@@ -257,14 +257,14 @@ class Organization extends OrganizationModel {
                         && ($o=Organization::lookup(array('name'=>$f->getClean())))
                         && $o->id != $this->getId()) {
                 $valid = false;
-                $f->addError('Organization with the same name already exists');
+                $f->addError(__('Organization with the same name already exists'));
             }
         }
 
         if ($vars['domain']) {
             foreach (explode(',', $vars['domain']) as $d) {
                 if (!Validator::is_email('t@' . trim($d))) {
-                    $errors['domain'] = 'Enter a valid email domain, like domain.com';
+                    $errors['domain'] = __('Enter a valid email domain, like domain.com');
                 }
             }
         }
@@ -279,7 +279,7 @@ class Organization extends OrganizationModel {
                         && $team = Team::lookup(substr($vars['manager'], 1)))
                     break;
             default:
-                $errors['manager'] = 'Select a staff member or team from the list';
+                $errors['manager'] = __('Select an agent or team from the list');
             }
         }
 
@@ -318,6 +318,11 @@ class Organization extends OrganizationModel {
             }
         }
 
+        // Send signal for search engine updating if not modifying the
+        // fields specific to the organization
+        if (count($this->dirty) === 0)
+            Signal::send('model.updated', $this);
+
         return $this->save();
     }
 
@@ -349,7 +354,7 @@ class Organization extends OrganizationModel {
         if (($field=$form->getField('name'))
                 && $field->getClean()
                 && Organization::lookup(array('name' => $field->getClean()))) {
-            $field->addError('Organization with the same name already exists');
+            $field->addError(__('Organization with the same name already exists'));
             $valid = false;
         }
 
@@ -428,16 +433,16 @@ class OrganizationForm extends DynamicForm {
     }
 
 }
-Filter::addSupportedMatches('Organization Data', function() {
+Filter::addSupportedMatches(/*@trans*/ 'Organization Data', function() {
     $matches = array();
     foreach (OrganizationForm::getInstance()->getFields() as $f) {
         if (!$f->hasData())
             continue;
-        $matches['field.'.$f->get('id')] = 'Organization / '.$f->getLabel();
-        if (($fi = $f->getImpl()) instanceof SelectionField) {
-            foreach ($fi->getList()->getProperties() as $p) {
+        $matches['field.'.$f->get('id')] = __('Organization').' / '.$f->getLabel();
+        if (($fi = $f->getImpl()) && $fi->hasSubFields()) {
+            foreach ($fi->getSubFields() as $p) {
                 $matches['field.'.$f->get('id').'.'.$p->get('id')]
-                    = 'Organization / '.$f->getLabel().' / '.$p->getLabel();
+                    = __('Organization').' / '.$f->getLabel().' / '.$p->getLabel();
             }
         }
     }
diff --git a/include/class.orm.php b/include/class.orm.php
index 29d474c4ce63927ba5dcbde56223d97fe7147a92..12bc89072444fbbcb44032f2afb5818d9fe3619a 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -42,14 +42,18 @@ class VerySimpleModel {
         elseif (isset(static::$meta['joins'][$field])) {
             // TODO: Support instrumented lists and such
             $j = static::$meta['joins'][$field];
-            $class = $j['fkey'][0];
-            $v = $this->ht[$field] = $class::lookup(
-                array($j['fkey'][1] => $this->ht[$j['local']]));
-            return $v;
+            // Make sure joins were inspected
+            if (isset($j['fkey'])
+                    && ($class = $j['fkey'][0])
+                    && class_exists($class)) {
+                $v = $this->ht[$field] = $class::lookup(
+                    array($j['fkey'][1] => $this->ht[$j['local']]));
+                return $v;
+            }
         }
         if (isset($default))
             return $default;
-        throw new OrmException(sprintf('%s: %s: Field not defined',
+        throw new OrmException(sprintf(__('%s: %s: Field not defined'),
             get_class($this), $field));
     }
     function __get($field) {
@@ -86,7 +90,7 @@ class VerySimpleModel {
             }
             else
                 throw new InvalidArgumentException(
-                    'Expecting NULL or instance of ' . $j['fkey'][0]);
+                    sprintf(__('Expecting NULL or instance of %s'), $j['fkey'][0]));
 
             // Capture the foreign key id value
             $field = $j['local'];
@@ -132,7 +136,7 @@ class VerySimpleModel {
     static function _inspect() {
         if (!static::$meta['table'])
             throw new OrmConfigurationException(
-                'Model does not define meta.table', get_called_class());
+                __('Model does not define meta.table'), get_called_class());
 
         // Break down foreign-key metadata
         foreach (static::$meta['joins'] as $field => &$j) {
@@ -141,8 +145,10 @@ class VerySimpleModel {
                 $info = $model::$meta['joins'][$key];
                 $constraint = array();
                 if (!is_array($info['constraint']))
-                    throw new OrmConfigurationException(
-                        $j['reverse'].': Reverse does not specify any constraints');
+                    throw new OrmConfigurationException(sprintf(__(
+                        // `reverse` here is the reverse of an ORM relationship
+                        '%s: Reverse does not specify any constraints'),
+                        $j['reverse']));
                 foreach ($info['constraint'] as $foreign => $local) {
                     list(,$field) = explode('.', $local);
                     $constraint[$field] = "$model.$foreign";
@@ -260,6 +266,12 @@ class SqlFunction {
         $args = (count($this->args)) ? implode(',', db_input($this->args)) : "";
         return sprintf('%s(%s)', $this->func, $args);
     }
+
+    static function __callStatic($func, $args) {
+        $I = new static($func);
+        $I->args = $args;
+        return $I;
+    }
 }
 
 class QuerySet implements IteratorAggregate, ArrayAccess {
@@ -272,6 +284,10 @@ class QuerySet implements IteratorAggregate, ArrayAccess {
     var $offset = 0;
     var $related = array();
     var $values = array();
+    var $lock = false;
+
+    const LOCK_EXCLUSIVE = 1;
+    const LOCK_SHARED = 2;
 
     var $compiler = 'MySqlCompiler';
     var $iterator = 'ModelInstanceIterator';
@@ -299,6 +315,11 @@ class QuerySet implements IteratorAggregate, ArrayAccess {
         return $this;
     }
 
+    function lock($how=false) {
+        $this->lock = $how ?: self::LOCK_EXCLUSIVE;
+        return $this;
+    }
+
     function limit($count) {
         $this->limit = $count;
         return $this;
@@ -383,10 +404,10 @@ class QuerySet implements IteratorAggregate, ArrayAccess {
         return $this->getIterator()->offsetGet($offset);
     }
     function offsetUnset($a) {
-        throw new Exception('QuerySet is read-only');
+        throw new Exception(__('QuerySet is read-only'));
     }
     function offsetSet($a, $b) {
-        throw new Exception('QuerySet is read-only');
+        throw new Exception(__('QuerySet is read-only'));
     }
 
     function __toString() {
@@ -478,10 +499,10 @@ class ModelInstanceIterator implements Iterator, ArrayAccess {
         return $this->cache[$offset];
     }
     function offsetUnset($a) {
-        throw new Exception(sprintf('%s is read-only', get_class($this)));
+        throw new Exception(sprintf(__('%s is read-only'), get_class($this)));
     }
     function offsetSet($a, $b) {
-        throw new Exception(sprintf('%s is read-only', get_class($this)));
+        throw new Exception(sprintf(__('%s is read-only'), get_class($this)));
     }
 }
 
@@ -519,7 +540,7 @@ class InstrumentedList extends ModelInstanceIterator {
 
     function add($object, $at=false) {
         if (!$object || !$object instanceof $this->model)
-            throw new Exception('Attempting to add invalid object to list');
+            throw new Exception(__('Attempting to add invalid object to list'));
 
         $object->set($this->key, $this->id);
         $object->save();
@@ -787,7 +808,7 @@ class MySqlCompiler extends SqlCompiler {
         'lt' => '%1$s < %2$s',
         'gte' => '%1$s >= %2$s',
         'lte' => '%1$s <= %2$s',
-        'isnull' => '%1$s IS NULL',
+        'isnull' => array('self', '__isnull'),
         'like' => '%1$s LIKE %2$s',
         'hasbit' => '%1$s & %2$s != 0',
         'in' => array('self', '__in'),
@@ -809,6 +830,12 @@ class MySqlCompiler extends SqlCompiler {
         return sprintf('%s IN (%s)', $a, $b);
     }
 
+    function __isnull($a, $b) {
+        return $b
+            ? sprintf('%s IS NULL', $a)
+            : sprintf('%s IS NOT NULL', $a);
+    }
+
     function compileJoin($tip, $model, $alias, $info) {
         $constraints = array();
         $join = ' JOIN ';
@@ -932,6 +959,14 @@ class MySqlCompiler extends SqlCompiler {
             $sql .= ' LIMIT '.$queryset->limit;
         if ($queryset->offset)
             $sql .= ' OFFSET '.$queryset->offset;
+        switch ($queryset->lock) {
+        case QuerySet::LOCK_EXCLUSIVE:
+            $sql .= ' FOR UPDATE';
+            break;
+        case QuerySet::LOCK_SHARED:
+            $sql .= ' LOCK IN SHARE MODE';
+            break;
+        }
 
         return new MysqlExecutor($sql, $this->params);
     }
@@ -1002,7 +1037,7 @@ class MysqlExecutor {
 
     function _bind($params) {
         if (count($params) != $this->stmt->param_count)
-            throw new Exception('Parameter count does not match query');
+            throw new Exception(__('Parameter count does not match query'));
 
         $types = '';
         $ps = array();
diff --git a/include/class.osticket.php b/include/class.osticket.php
index 71a8c94d142878adfaa9d3966868147b24e09515..d7e8f3d98a4386762d0071294f6dfb982f4a070e 100644
--- a/include/class.osticket.php
+++ b/include/class.osticket.php
@@ -111,7 +111,6 @@ class osTicket {
     }
 
     function checkCSRFToken($name='') {
-
         $name = $name?$name:$this->getCSRF()->getTokenName();
         if(isset($_POST[$name]) && $this->validateCSRFToken($_POST[$name]))
             return true;
@@ -119,9 +118,9 @@ class osTicket {
         if(isset($_SERVER['HTTP_X_CSRFTOKEN']) && $this->validateCSRFToken($_SERVER['HTTP_X_CSRFTOKEN']))
             return true;
 
-        $msg=sprintf('Invalid CSRF token [%s] on %s',
+        $msg=sprintf(__('Invalid CSRF token [%1$s] on %2$s'),
                 ($_POST[$name].''.$_SERVER['HTTP_X_CSRFTOKEN']), THISPAGE);
-        $this->logWarning('Invalid CSRF Token '.$name, $msg, false);
+        $this->logWarning(__('Invalid CSRF Token').' '.$name, $msg, false);
 
         return false;
     }
@@ -134,24 +133,6 @@ class osTicket {
             return ($token && !strcasecmp($token, $this->getLinkToken()));
     }
 
-    function isFileTypeAllowed($file, $mimeType='') {
-
-        if(!$file || !($allowedFileTypes=$this->getConfig()->getAllowedFileTypes()))
-            return false;
-
-        //Return true if all file types are allowed (.*)
-        if(trim($allowedFileTypes)=='.*') return true;
-
-        $allowed = array_map('trim', explode(',', strtolower($allowedFileTypes)));
-        $filename = is_array($file)?$file['name']:$file;
-
-        $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
-
-        //TODO: Check MIME type - file ext. shouldn't be solely trusted.
-
-        return ($ext && is_array($allowed) && in_array(".$ext", $allowed));
-    }
-
     /* Replace Template Variables */
     function replaceTemplateVariables($input, $vars=array()) {
 
@@ -247,7 +228,7 @@ class osTicket {
         if($email) {
             $email->sendAlert($to, $subject, $message, null, array('text'=>true, 'reply-tag'=>false));
         } else {//no luck - try the system mail.
-            Mailer::sendmail($to, $subject, $message, sprintf('"osTicket Alerts"<%s>',$to));
+            Mailer::sendmail($to, $subject, $message, '"'.__('osTicket Alerts').sprintf('" <%s>',$to));
         }
 
         //log the alert? Watch out for loops here.
@@ -278,8 +259,9 @@ class osTicket {
             $alert =false;
 
         $e = new Exception();
-        $bt = str_replace(ROOT_DIR, '(root)/', $e->getTraceAsString());
-        $error .= nl2br("\n\n---- Backtrace ----\n".$bt);
+        $bt = str_replace(ROOT_DIR, _S(/* `root` is a root folder */ '(root)').'/',
+            $e->getTraceAsString());
+        $error .= nl2br("\n\n---- "._S('Backtrace')." ----\n".$bt);
 
         return $this->log(LOG_ERR, $title, $error, $alert);
     }
@@ -444,6 +426,8 @@ class osTicket {
 
     /**** static functions ****/
     function start() {
+        // Prep basic translation support
+        Internationalization::bootstrap();
 
         if(!($ost = new osTicket()))
             return null;
@@ -455,6 +439,9 @@ class osTicket {
         // Bootstrap installed plugins
         $ost->plugins->bootstrap();
 
+        // Mirror content updates to the search backend
+        $ost->searcher = new SearchInterface();
+
         return $ost;
     }
 }
diff --git a/include/class.page.php b/include/class.page.php
index 8980bc273b79cab5a781bc7dbe0ba9ada201c682..c533617d6126b04668d5927bf43cacf875c34cb8 100644
--- a/include/class.page.php
+++ b/include/class.page.php
@@ -105,8 +105,8 @@ class Page {
     function update($vars, &$errors) {
 
         if(!$vars['isactive'] && $this->isInUse()) {
-            $errors['err'] = 'A page currently in-use CANNOT be disabled!';
-            $errors['isactive'] = 'Page is in-use!';
+            $errors['err'] = __('A page currently in-use CANNOT be disabled!');
+            $errors['isactive'] = __('Page is in-use!');
         }
 
         if($errors || !$this->save($this->getId(), $vars, $errors))
@@ -236,18 +236,18 @@ class Page {
 
         //validate
         if($id && $id!=$vars['id'])
-            $errors['err'] = 'Internal error. Try again';
+            $errors['err'] = __('Internal error. Try again');
 
         if(!$vars['type'])
-            $errors['type'] = 'Type required';
+            $errors['type'] = __('Type is required');
 
         if(!$vars['name'])
-            $errors['name'] = 'Name required';
+            $errors['name'] = __('Name is required');
         elseif(($pid=self::getIdByName($vars['name'])) && $pid!=$id)
-            $errors['name'] = 'Name already exists';
+            $errors['name'] = __('Name already exists');
 
         if(!$vars['body'])
-            $errors['body'] = 'Page body is required';
+            $errors['body'] = __('Page body is required');
 
         if($errors) return false;
 
@@ -264,12 +264,13 @@ class Page {
             if(db_query($sql))
                 return true;
 
-            $errors['err']='Unable to update page.';
+            $errors['err']=sprintf(__('Unable to update %s.'), __('this site page'));
 
         } else {
             $sql='INSERT INTO '.PAGE_TABLE.' SET '.$sql.', created=NOW()';
             if (!db_query($sql) || !($id=db_insert_id())) {
-                $errors['err']='Unable to create page. Internal error';
+                $errors['err']=sprintf(__('Unable to create %s.'), __('this site page'))
+                   .' '.__('Internal error occurred');
                 return false;
             }
 
diff --git a/include/class.pagenate.php b/include/class.pagenate.php
index 67cbc0e7170ea09d34f5ee1320936f3113bfedcb..361d21f89855a4982661ffdf6421bbeae360a203 100644
--- a/include/class.pagenate.php
+++ b/include/class.pagenate.php
@@ -13,14 +13,15 @@
 
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
+
 class PageNate {
-    
+
     var $start;
     var $limit;
     var $total;
     var $page;
     var $pages;
-    
+
 
     function PageNate($total,$page,$limit=20,$url='') {
         $this->total = intval($total);
@@ -28,7 +29,7 @@ class PageNate {
         $this->page  = max($page, 1 );
         $this->start = max((($page-1)*$this->limit),0);
         $this->pages = ceil( $this->total / $this->limit );
-        
+
         if (($this->limit > $this->total) || ($this->page>ceil($this->total/$this->limit))) {
             $this->start = 0;
         }
@@ -54,16 +55,16 @@ class PageNate {
     function getLimit() {
         return $this->limit;
     }
-    
-    
+
+
     function getNumPages(){
         return $this->pages;
     }
-  
+
     function getPage() {
         return ceil(($this->start+1)/$this->limit);
     }
-    
+
     function showing() {
         $html = '';
         $from= $this->start+1;
@@ -72,15 +73,16 @@ class PageNate {
         } else {
             $to= $this->total;
         }
-        $html="&nbsp;Showing&nbsp;&nbsp;";
+        $html="&nbsp;".__('Showing')."&nbsp;&nbsp;";
         if ($this->total > 0) {
-            $html .= "$from - $to of " .$this->total;
+            $html .= sprintf(__('%1$d - %2$d of %3$d' /* Used in pagination output */),
+               $from, $to, $this->total);
         }else{
             $html .= " 0 ";
         }
         return $html;
     }
-    
+
     function getPageLinks() {
         $html                 = '';
         $file                =$this->url;
@@ -90,15 +92,15 @@ class PageNate {
 
         $last=$this_page-1;
         $next=$this_page+1;
-        
+
         $start_loop         = floor($this_page-$displayed_span);
         $stop_loop          = ceil($this_page + $displayed_span);
-       
-        
-        
+
+
+
         $stopcredit    =($start_loop<1)?0-$start_loop:0;
         $startcredit   =($stop_loop>$total_pages)?$stop_loop-$total_pages:0;
-        
+
         $start_loop =($start_loop-$startcredit>0)?$start_loop-$startcredit:1;
         $stop_loop  =($stop_loop+$stopcredit>$total_pages)?$total_pages:$stop_loop+$stopcredit;
 
@@ -106,7 +108,7 @@ class PageNate {
             $lastspan=($start_loop-$displayed_span>0)?$start_loop-$displayed_span:1;
             $html .= "\n<a href=\"$file&p=$lastspan\" ><strong>&laquo;</strong></a>";
         }
-        
+
         for ($i=$start_loop; $i <= $stop_loop; $i++) {
             $page = ($i - 1) * $this->limit;
             if ($i == $this_page) {
@@ -119,9 +121,9 @@ class PageNate {
             $nextspan=($stop_loop+$displayed_span>$total_pages)?$total_pages-$displayed_span:$stop_loop+$displayed_span;
             $html .= "\n<a href=\"$file&p=$nextspan\" ><strong>&raquo;</strong></a>";
         }
-        
 
-        
+
+
         return $html;
     }
 
diff --git a/include/class.pdf.php b/include/class.pdf.php
index 8f41ec2db867a3e6487b664d7aac37a62ab6a763..f0422b47bc5e242b89c56c52266b35948f066eda 100644
--- a/include/class.pdf.php
+++ b/include/class.pdf.php
@@ -97,9 +97,10 @@ class Ticket2PDF extends mPDF
 		$this->SetY(-15);
         $this->WriteCell(0, 2, '', "T", 2, 'L');
 		$this->SetFont('Arial', 'I', 9);
-		$this->WriteCell(0, 7, 'Ticket #'.$this->getTicket()->getNumber().' printed by '.$thisstaff->getUserName().' on '.date('r'), 0, 0, 'L');
+        $this->WriteCell(0, 7, sprintf(__('Ticket #%1$s printed by %2$s on %3$s'),
+            $this->getTicket()->getNumber(), $thisstaff->getUserName(), date('r')), 0, 0, 'L');
 		//$this->WriteCell(0,10,'Page '.($this->PageNo()-$this->pageOffset).' of {nb} '.$this->pageOffset.' '.$this->PageNo(),0,0,'R');
-		$this->WriteCell(0, 7, 'Page ' . ($this->PageNo() - $this->pageOffset), 0, 0, 'R');
+		$this->WriteCell(0, 7, sprintf(__('Page %d'), ($this->PageNo() - $this->pageOffset)), 0, 0, 'R');
 	}
 
     function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='') {
@@ -149,7 +150,7 @@ class Ticket2PDF extends mPDF
         $this->cMargin = 0;
         $this->SetFont('Arial', 'B', 11);
         $this->SetTextColor(10, 86, 142);
-        $this->WriteCell($w, 7,'Ticket #'.$ticket->getNumber(), 0, 0, 'L');
+        $this->WriteCell($w, 7,sprintf(__('Ticket #%s'),$ticket->getNumber()), 0, 0, 'L');
         $this->Ln(7);
         $this->cMargin = 3;
         $this->SetTextColor(0);
@@ -157,35 +158,35 @@ class Ticket2PDF extends mPDF
         $this->SetFillColor(244, 250, 255);
         $this->SetX($this->lMargin);
         $this->SetFont('Arial', 'B', 11);
-        $this->WriteCell($l, 7, 'Status', 1, 0, 'L', true);
+        $this->WriteCell($l, 7, __('Status'), 1, 0, 'L', true);
         $this->SetFont('');
-        $this->WriteCell($c, 7, $ticket->getStatus(), 1, 0, 'L', true);
+        $this->WriteCell($c, 7, (string)$ticket->getStatus(), 1, 0, 'L', true);
         $this->SetFont('Arial', 'B', 11);
-        $this->WriteCell($l, 7, 'Name', 1, 0, 'L', true);
+        $this->WriteCell($l, 7, __('Name'), 1, 0, 'L', true);
         $this->SetFont('');
         $this->WriteCell($c, 7, (string)$ticket->getName(), 1, 1, 'L', true);
         $this->SetFont('Arial', 'B', 11);
-        $this->WriteCell($l, 7, 'Priority', 1, 0, 'L', true);
+        $this->WriteCell($l, 7, __('Priority'), 1, 0, 'L', true);
         $this->SetFont('');
         $this->WriteCell($c, 7, $ticket->getPriority(), 1, 0, 'L', true);
         $this->SetFont('Arial', 'B', 11);
-        $this->WriteCell($l, 7, 'Email', 1, 0, 'L', true);
+        $this->WriteCell($l, 7, __('Email'), 1, 0, 'L', true);
         $this->SetFont('');
         $this->WriteCell($c, 7, $ticket->getEmail(), 1, 1, 'L', true);
         $this->SetFont('Arial', 'B', 11);
-        $this->WriteCell($l, 7, 'Department', 1, 0, 'L', true);
+        $this->WriteCell($l, 7, __('Department'), 1, 0, 'L', true);
         $this->SetFont('');
         $this->WriteCell($c, 7, $ticket->getDeptName(), 1, 0, 'L', true);
         $this->SetFont('Arial', 'B', 11);
-        $this->WriteCell($l, 7, 'Phone', 1, 0, 'L', true);
+        $this->WriteCell($l, 7, __('Phone'), 1, 0, 'L', true);
         $this->SetFont('');
         $this->WriteCell($c, 7, $ticket->getPhoneNumber(), 1, 1, 'L', true);
         $this->SetFont('Arial', 'B', 11);
-        $this->WriteCell($l, 7, 'Create Date', 1, 0, 'L', true);
+        $this->WriteCell($l, 7, __('Create Date'), 1, 0, 'L', true);
         $this->SetFont('');
         $this->WriteCell($c, 7, Format::db_datetime($ticket->getCreateDate()), 1, 0, 'L', true);
         $this->SetFont('Arial', 'B', 11);
-        $this->WriteCell($l, 7, 'Source', 1, 0, 'L', true);
+        $this->WriteCell($l, 7, __('Source'), 1, 0, 'L', true);
         $this->SetFont('');
         $source = ucfirst($ticket->getSource());
         if($ticket->getIP())
@@ -195,46 +196,46 @@ class Ticket2PDF extends mPDF
 
         $this->SetFont('Arial', 'B', 11);
         if($ticket->isOpen()) {
-            $this->WriteCell($l, 7, 'Assigned To', 1, 0, 'L', true);
+            $this->WriteCell($l, 7, __('Assigned To'), 1, 0, 'L', true);
             $this->SetFont('');
             $this->WriteCell($c, 7, $ticket->isAssigned()?$ticket->getAssigned():' -- ', 1, 0, 'L', true);
         } else {
 
-            $closedby = 'unknown';
+            $closedby = __('unknown');
             if(($staff = $ticket->getStaff()))
                 $closedby = (string) $staff->getName();
 
-            $this->WriteCell($l, 7, 'Closed By', 1, 0, 'L', true);
+            $this->WriteCell($l, 7, __('Closed By'), 1, 0, 'L', true);
             $this->SetFont('');
             $this->WriteCell($c, 7, $closedby, 1, 0, 'L', true);
         }
 
         $this->SetFont('Arial', 'B', 11);
-        $this->WriteCell($l, 7, 'Help Topic', 1, 0, 'L', true);
+        $this->WriteCell($l, 7, __('Help Topic'), 1, 0, 'L', true);
         $this->SetFont('');
         $this->WriteCell($c, 7, $ticket->getHelpTopic(), 1, 1, 'L', true);
         $this->SetFont('Arial', 'B', 11);
-        $this->WriteCell($l, 7, 'SLA Plan', 1, 0, 'L', true);
+        $this->WriteCell($l, 7, __('SLA Plan'), 1, 0, 'L', true);
         $this->SetFont('');
         $sla = $ticket->getSLA();
         $this->WriteCell($c, 7, $sla?$sla->getName():' -- ', 1, 0, 'L', true);
         $this->SetFont('Arial', 'B', 11);
-        $this->WriteCell($l, 7, 'Last Response', 1, 0, 'L', true);
+        $this->WriteCell($l, 7, __('Last Response'), 1, 0, 'L', true);
         $this->SetFont('');
         $this->WriteCell($c, 7, Format::db_datetime($ticket->getLastRespDate()), 1, 1, 'L', true);
         $this->SetFont('Arial', 'B', 11);
         if($ticket->isOpen()) {
-            $this->WriteCell($l, 7, 'Due Date', 1, 0, 'L', true);
+            $this->WriteCell($l, 7, __('Due Date'), 1, 0, 'L', true);
             $this->SetFont('');
             $this->WriteCell($c, 7, Format::db_datetime($ticket->getEstDueDate()), 1, 0, 'L', true);
         } else {
-            $this->WriteCell($l, 7, 'Close Date', 1, 0, 'L', true);
+            $this->WriteCell($l, 7, __('Close Date'), 1, 0, 'L', true);
             $this->SetFont('');
             $this->WriteCell($c, 7, Format::db_datetime($ticket->getCloseDate()), 1, 0, 'L', true);
         }
 
         $this->SetFont('Arial', 'B', 11);
-        $this->WriteCell($l, 7, 'Last Message', 1, 0, 'L', true);
+        $this->WriteCell($l, 7, __('Last Message'), 1, 0, 'L', true);
         $this->SetFont('');
         $this->WriteCell($c, 7, Format::db_datetime($ticket->getLastMsgDate()), 1, 1, 'L', true);
 
@@ -304,6 +305,7 @@ class Ticket2PDF extends mPDF
 
                     if ($files)
                         $text.="<div>Files Attached: [".implode(', ',$files)."]</div>";
+                        $text.="<div>".sprintf(__('Files Attached: [%s]'),implode(', ',$files))."</div>";
                 }
                 $this->WriteHtml('<div class="thread-body">'.$text.'</div>', 2, false, false);
                 $this->Ln(5);
diff --git a/include/class.plugin.php b/include/class.plugin.php
index ffa86b614161cb15b9ea9b418155b1d7f2c0e208..a6b653697e1b8ac5e3d60cc7ee96d8e5e59ad43b 100644
--- a/include/class.plugin.php
+++ b/include/class.plugin.php
@@ -22,6 +22,10 @@ class PluginConfig extends Config {
         return array();
     }
 
+    function hasCustomConfig() {
+        return $this instanceof PluginCustomConfig;
+    }
+
     /**
      * Retreive a Form instance for the configurable options offered in
      * ::getOptions
@@ -55,6 +59,17 @@ class PluginConfig extends Config {
      * array.
      */
     function commit(&$errors=array()) {
+        global $msg;
+
+        if ($this->hasCustomConfig())
+            return $this->saveCustomConfig($errors);
+
+        return $this->commitForm($errors);
+    }
+
+    function commitForm(&$errors=array()) {
+        global $msg;
+
         $f = $this->getForm();
         $commit = false;
         if ($f->isValid()) {
@@ -68,7 +83,11 @@ class PluginConfig extends Config {
                 $field = $f->getField($name);
                 $dbready[$name] = $field->to_database($val);
             }
-            return $this->updateAll($dbready);
+            if ($this->updateAll($dbready)) {
+                if (!$msg)
+                    $msg = 'Successfully updated configuration';
+                return true;
+            }
         }
         return false;
     }
@@ -93,6 +112,20 @@ class PluginConfig extends Config {
     }
 }
 
+/**
+ * Interface: PluginCustomConfig
+ *
+ * Allows a plugin to specify custom configuration pages. If the
+ * configuration cannot be suited by a single page, single form, then
+ * the plugin can use the ::renderCustomConfig() method to trigger
+ * rendering the page, and use ::saveCustomConfig() to trigger
+ * validating and saving the custom configuration.
+ */
+interface PluginCustomConfig {
+    function renderCustomConfig();
+    function saveCustomConfig();
+}
+
 class PluginManager {
     static private $plugin_info = array();
     static private $plugin_list = array();
@@ -309,6 +342,15 @@ abstract class Plugin {
     var $id;
     var $info;
 
+    const VERIFIED = 1;             // Thumbs up
+    const VERIFY_EXT_MISSING = 2;   // PHP extension missing
+    const VERIFY_FAILED = 3;        // Bad signature data
+    const VERIFY_ERROR = 4;         // Unable to verify (unexpected error)
+    const VERIFY_NO_KEY = 5;        // Public key missing
+    const VERIFY_DNS_PASS = 6;      // DNS check passes, cannot verify sig
+
+    static $verify_domain = 'updates.osticket.com';
+
     function Plugin($id) {
         $this->id = $id;
         $this->load();
@@ -324,10 +366,11 @@ abstract class Plugin {
     }
 
     function getId() { return $this->id; }
-    function getName() { return $this->info['name']; }
+    function getName() { return $this->__($this->info['name']); }
     function isActive() { return $this->ht['isactive']; }
     function isPhar() { return $this->ht['isphar']; }
     function getInstallDate() { return $this->ht['installed']; }
+    function getInstallPath() { return $this->ht['install_path']; }
 
     function getIncludePath() {
         return realpath(INCLUDE_DIR . $this->info['install_path'] . '/'
@@ -422,6 +465,222 @@ abstract class Plugin {
         if ($path)
            return PluginManager::getInstance($path);
     }
+
+    /**
+     * Function: isVerified
+     *
+     * This will help verify the content, integrity, oversight, and origin
+     * of plugins, language packs and other modules distributed for
+     * osTicket.
+     *
+     * This idea is that the signature of the PHAR file will be registered
+     * in DNS, for instance,
+     * `7afc8bf80b0555bed88823306744258d6030f0d9.updates.osticket.com`, for
+     * a PHAR file with a SHA1 signature of
+     * `7afc8bf80b0555bed88823306744258d6030f0d9 `, which will resolve to a
+     * string like the following:
+     * ```
+     * "v=1; i=storage:s3; s=MEUCIFw6A489eX4Oq17BflxCZ8+MH6miNjtcpScUoKDjmb
+     * lsAiEAjiBo9FzYtV3WQtW6sbhPlJXcoPpDfYyQB+BFVBMps4c=; V=0.1;"
+     * ```
+     * Which is a simple semicolon separated key-value pair string with the
+     * following keys
+     *
+     *   Key | Description
+     *  :----|:---------------------------------------------------
+     *   v   | Algorithm version
+     *   i   | Plugin 'id' registered in plugin.php['id']
+     *   V   | Plugin 'version' registered in plugin.php['version']
+     *   s   | OpenSSL signature of the PHAR SHA1 signature using a
+     *       | private key (specified on the command line)
+     *
+     * The public key, which will be distributed with osTicket, can be used
+     * to verify the signature of the PHAR file from the data received from
+     * DNS.
+     *
+     * Parameters:
+     * $phar - (string) filename of phar file to verify
+     *
+     * Returns:
+     * (int) -
+     *      Plugin::VERIFIED upon success
+     *      Plugin::VERIFY_DNS_PASS if found in DNS but cannot verify sig
+     *      Plugin::VERIFY_NO_KEY if public key not found in include/plugins
+     *      Plugin::VERIFY_FAILED if the plugin fails validation
+     *      Plugin::VERIFY_EXT_MISSING if a PHP extension is required
+     *      Plugin::VERIFY_ERROR if an unexpected error occurred
+     */
+    static function isVerified($phar) {
+        static $pubkey = null;
+
+        if (!class_exists('Phar'))
+            return self::VERIFY_EXT_MISSING;
+        elseif (!file_exists(INCLUDE_DIR . '/plugins/updates.pem'))
+            return self::VERIFY_NO_KEY;
+
+        if (!isset($pubkey)) {
+            $pubkey = openssl_pkey_get_public(
+                    file_get_contents(INCLUDE_DIR . 'plugins/updates.pem'));
+        }
+        if (!$pubkey) {
+            return self::VERIFY_ERROR;
+        }
+
+        require_once(PEAR_DIR.'Net/DNS2.php');
+        $P = new Phar($phar);
+        $sig = $P->getSignature();
+        $info = array();
+        try {
+            $q = new Net_DNS2_Resolver();
+            $r = $q->query(strtolower($sig['hash']) . '.' . self::$verify_domain, 'TXT');
+            foreach ($r->answer as $rec) {
+                foreach ($rec->text as $txt) {
+                    foreach (explode(';', $txt) as $kv) {
+                        list($k, $v) = explode('=', trim($kv));
+                        $info[$k] = trim($v);
+                    }
+                    if ($info['v'] && $info['s'])
+                        break;
+                }
+            }
+        }
+        catch (Net_DNS2_Exception $e) {
+            // TODO: Differenciate NXDOMAIN and DNS failure
+        }
+
+        if (is_array($info) && isset($info['v'])) {
+            switch ($info['v']) {
+            case '1':
+                if (!($signature = base64_decode($info['s'])))
+                    return self::VERIFY_FAILED;
+                elseif (!function_exists('openssl_verify'))
+                    return self::VERIFY_DNS_PASS;
+
+                $codes = array(
+                    -1 => self::VERIFY_ERROR,
+                    0 => self::VERIFY_FAILED,
+                    1 => self::VERIFIED,
+                );
+                $result = openssl_verify($sig['hash'], $signature, $pubkey,
+                    OPENSSL_ALGO_SHA1);
+                return $codes[$result];
+            }
+        }
+        return self::VERIFY_FAILED;
+    }
+
+    static function showVerificationBadge($phar) {
+        switch (self::isVerified($phar)) {
+        case self::VERIFIED:
+            $show_lock = true;
+        case self::VERIFY_DNS_PASS: ?>
+        &nbsp;
+        <span class="label label-verified" title="<?php
+            if ($show_lock) echo sprintf(__('Verified by %s'), self::$verify_domain);
+            ?>"> <?php
+            if ($show_lock) echo '<i class="icon icon-lock"></i>'; ?>
+            <?php echo $show_lock ? __('Verified') : __('Registered'); ?></span>
+<?php       break;
+        case self::VERIFY_FAILED: ?>
+        &nbsp;
+        <span class="label label-danger" title="<?php
+            echo __('The originator of this extension cannot be verified');
+            ?>"><i class="icon icon-warning-sign"></i></span>
+<?php       break;
+        }
+    }
+
+    /**
+     * Function: __
+     *
+     * Translate a single string (without plural alternatives) from the
+     * langauge pack installed in this plugin. The domain is auto-configured
+     * and detected from the plugin install path.
+     */
+    function __($msgid) {
+        if (!isset($this->translation)) {
+            // Detect the domain from the plugin install-path
+            $groups = array();
+            preg_match('`plugins/(\w+)(?:.phar)?`', $this->getInstallPath(), $groups);
+
+            $domain = $groups[1];
+            if (!$domain)
+                return $msgid;
+
+            $this->translation = self::translate($domain);
+        }
+        list($__, $_N) = $this->translation;
+        return $__($msgid);
+    }
+
+    // Domain-specific translations (plugins)
+    /**
+     * Function: translate
+     *
+     * Convenience function to setup translation functions for other
+     * domains. This is of greatest benefit for plugins. This will return
+     * two functions to perform the translations. The first will translate a
+     * single string, the second will translate a plural string.
+     *
+     * Parameters:
+     * $domain - (string) text domain. The location of the MO.php file
+     *      will be (path)/LC_MESSAGES/(locale)/(domain).mo.php. The (path)
+     *      can be set via the $options parameter
+     * $options - (array<string:mixed>) Extra options for the setup
+     *      "path" - (string) path to the folder containing the LC_MESSAGES
+     *          folder. The (locale) setting is set externally respective to
+     *          the user. If this is not set, the directory of the caller is
+     *          assumed, plus '/i18n'.  This is geared for plugins to be
+     *          built with i18n content inside the '/i18n/' folder.
+     *
+     * Returns:
+     * Translation utility functions which mimic the __() and _N()
+     * functions. Note that two functions are returned. Capture them with a
+     * PHP list() construct.
+     *
+     * Caveats:
+     * When desiging plugins which might be installed in versions of
+     * osTicket which don't provide this function, use this compatibility
+     * interface:
+     *
+     * // Provide compatibility function for versions of osTicket prior to
+     * // translation support (v1.9.4)
+     * function translate($domain) {
+     *     if (!method_exists('Plugin', 'translate')) {
+     *         return array(
+     *             function($x) { return $x; },
+     *             function($x, $y, $n) { return $n != 1 ? $y : $x; },
+     *         );
+     *     }
+     *     return Plugin::translate($domain);
+     * }
+     */
+    static function translate($domain, $options=array()) {
+
+        // Configure the path for the domain. If no
+        $path = @$options['path'];
+        if (!$path) {
+            # Fetch the working path of the caller
+            $bt = debug_backtrace(false);
+            $path = dirname($bt[0]["file"]) . '/i18n';
+        }
+        $path = rtrim($path, '/') . '/';
+
+        $D = TextDomain::lookup($domain);
+        $D->setPath($path);
+        $trans = $D->getTranslation();
+
+        return array(
+            // __()
+            function($msgid) use ($trans) {
+                return $trans->translate($msgid);
+            },
+            // _N()
+            function($singular, $plural, $n) use ($trans) {
+                return $trans->ngettext($singular, $plural, $n);
+            },
+        );
+    }
 }
 
 ?>
diff --git a/include/class.pop3.php b/include/class.pop3.php
deleted file mode 100644
index 2ece6077c25bb2bb1784f3e9c7a2ddd2f4794e97..0000000000000000000000000000000000000000
--- a/include/class.pop3.php
+++ /dev/null
@@ -1,3 +0,0 @@
-<?php
-//No longer used. just used to clear the old file for now. Will be deleted in the upcoming versions.
-?>
diff --git a/include/class.search.php b/include/class.search.php
new file mode 100644
index 0000000000000000000000000000000000000000..c3b2b2c1e0cb2ef30aef3ddd43cadc53d2bf15d1
--- /dev/null
+++ b/include/class.search.php
@@ -0,0 +1,593 @@
+<?php
+/*********************************************************************
+    module.search.php
+
+    Search Engine for osTicket
+
+    This module defines the pieces for a search engine for osTicket.
+    Searching can be performed by various search engine backends which can
+    make use of the features of various search providers.
+
+    A reference search engine backend is provided which uses MySQL MyISAM
+    tables. This default backend should not be used on Galera clusters.
+
+    Jared Hancock <jared@osticket.com>
+    Peter Rotich <peter@osticket.com>
+    Copyright (c)  2006-2013 osTicket
+    http://www.osticket.com
+
+    Released under the GNU General Public License WITHOUT ANY WARRANTY.
+    See LICENSE.TXT for details.
+
+    vim: expandtab sw=4 ts=4 sts=4:
+**********************************************************************/
+
+abstract class SearchBackend {
+    static $id = false;
+    static $registry = array();
+
+    const SORT_RELEVANCE = 1;
+    const SORT_RECENT = 2;
+    const SORT_OLDEST = 3;
+
+    abstract function update($model, $id, $content, $new=false, $attrs=array());
+    abstract function find($query, $criteria, $model=false, $sort=array());
+
+    static function register($backend=false) {
+        $backend = $backend ?: get_called_class();
+
+        if ($backend::$id == false)
+            throw new Exception('SearchBackend must define an ID');
+
+        static::$registry[$backend::$id] = $backend;
+    }
+
+    function getInstance($id) {
+        if (!isset(self::$registry[$id]))
+            return null;
+
+        return new self::$registry[$id]();
+    }
+}
+
+// Register signals to intercept saving of various content throughout the
+// system
+
+class SearchInterface {
+
+    var $backend;
+
+    function __construct() {
+        $this->bootstrap();
+    }
+
+    function find($query, $criteria, $model=false, $sort=array()) {
+        $query = Format::searchable($query);
+        return $this->backend->find($query, $criteria, $model, $sort);
+    }
+
+    function update($model, $id, $content, $new=false, $attrs=array()) {
+        if (!$this->backend)
+            return;
+
+        $this->backend->update($model, $id, $content, $new, $attrs);
+    }
+
+    function createModel($model) {
+        return $this->updateModel($model, true);
+    }
+
+    function updateModel($model, $new=false) {
+        // The MySQL backend does not need to index attributes of the
+        // various models, because those other attributes are available in
+        // the local database in other tables.
+        switch (true) {
+        case $model instanceof ThreadEntry:
+            // Only index an entry for threads if a human created the
+            // content
+            if (!$model->getUserId() && !$model->getStaffId())
+                break;
+
+            $this->update($model, $model->getId(),
+                $model->getBody()->getSearchable(), $new,
+                array(
+                    'title' =>      $model->getTitle(),
+                    'ticket_id' =>  $model->getTicketId(),
+                    'created' =>    $model->getCreateDate(),
+                )
+            );
+            break;
+
+        case $model instanceof Ticket:
+            $cdata = array();
+            foreach ($model->loadDynamicData() as $a)
+                if ($v = $a->getSearchable())
+                    $cdata[] = $v;
+            $this->update($model, $model->getId(),
+                trim(implode("\n", $cdata)),
+                $new,
+                array(
+                    'title'=>       Format::searchable($model->getSubject()),
+                    'number'=>      $model->getNumber(),
+                    'status'=>      $model->getStatus(),
+                    'topic_id'=>    $model->getTopicId(),
+                    'priority_id'=> $model->getPriorityId(),
+                    // Stats (comments, attachments)
+                    // Access constraints
+                    'dept_id'=>     $model->getDeptId(),
+                    'staff_id'=>    $model->getStaffId(),
+                    'team_id'=>     $model->getTeamId(),
+                    // Sorting and ranging preferences
+                    'created'=>     $model->getCreateDate(),
+                    // Add last-updated timestamp
+                )
+            );
+            break;
+
+        case $model instanceof User:
+            $cdata = array();
+            foreach ($model->getDynamicData($false) as $e)
+                foreach ($e->getAnswers() as $tag=>$a)
+                    if ($tag != 'subject' && ($v = $a->getSearchable()))
+                        $cdata[] = $v;
+            $this->update($model, $model->getId(),
+                trim(implode("\n", $cdata)),
+                $new,
+                array(
+                    'title'=>       Format::searchable($model->getFullName()),
+                    'emails'=>      $model->emails->asArray(),
+                    'org_id'=>      $model->getOrgId(),
+                    'created'=>     $model->getCreateDate(),
+                )
+            );
+            break;
+
+        case $model instanceof Organization:
+            $cdata = array();
+            foreach ($model->getDynamicData(false) as $e)
+                foreach ($e->getAnswers() as $a)
+                    if ($v = $a->getSearchable())
+                        $cdata[] = $v;
+            $this->update($model, $model->getId(),
+                trim(implode("\n", $cdata)),
+                $new,
+                array(
+                    'title'=>       Format::searchable($model->getName()),
+                    'created'=>     $model->getCreateDate(),
+                )
+            );
+            break;
+
+        case $model instanceof FAQ:
+            $this->update($model, $model->getId(),
+                $model->getSearchableAnswer(),
+                $new,
+                array(
+                    'title'=>       Format::searchable($model->getQuestion()),
+                    'keywords'=>    $model->getKeywords(),
+                    'topics'=>      $model->getHelpTopicsIds(),
+                    'category_id'=> $model->getCategoryId(),
+                    'created'=>     $model->getCreateDate(),
+                )
+            );
+            break;
+
+        default:
+            // Not indexed
+            break;
+        }
+    }
+
+    function bootstrap() {
+        // Determine the backend
+        if (defined('SEARCH_BACKEND'))
+            $bk = SearchBackend::getInstance(SEARCH_BACKEND);
+
+        if (!$bk && !($bk = SearchBackend::getInstance('mysql')))
+            // No backend registered or defined
+            return false;
+
+        $this->backend = $bk;
+        $this->backend->bootstrap();
+
+        $self = $this;
+
+        // Thread entries
+        // Tickets, which can be edited as well
+        // Knowledgebase articles (FAQ and canned responses)
+        // Users, organizations
+        Signal::connect('model.created', array($this, 'createModel'));
+        Signal::connect('model.updated', array($this, 'updateModel'));
+        #Signal::connect('model.deleted', array($this, 'deleteModel'));
+    }
+}
+
+class MysqlSearchBackend extends SearchBackend {
+    static $id = 'mysql';
+    static $BATCH_SIZE = 30;
+
+    // Only index 20 batches per cron run
+    var $max_batches = 60;
+
+    function __construct() {
+        $this->SEARCH_TABLE = TABLE_PREFIX . '_search';
+    }
+
+    function bootstrap() {
+        Signal::connect('cron', array($this, 'IndexOldStuff'));
+    }
+
+    function update($model, $id, $content, $new=false, $attrs=array()) {
+        switch (true) {
+        case $model instanceof ThreadEntry:
+            $type = 'H';
+            break;
+        case $model instanceof Ticket:
+            $attrs['title'] = $attrs['number'].' '.$attrs['title'];
+            $type = 'T';
+            break;
+        case $model instanceof User:
+            $content .= implode("\n", $attrs['emails']);
+            $type = 'U';
+            break;
+        case $model instanceof Organization:
+            $type = 'O';
+            break;
+        case $model instanceof FAQ:
+            $type = 'K';
+            break;
+        case $model instanceof AttachmentFile:
+            $type = 'F';
+            break;
+        default:
+            // Not indexed
+            return;
+        }
+
+        $title = $attrs['title'] ?: '';
+
+        if (!$content && !$title)
+            return;
+
+        $sql = 'REPLACE INTO '.$this->SEARCH_TABLE
+            . ' SET object_type='.db_input($type)
+            . ', object_id='.db_input($id)
+            . ', content='.db_input($content)
+            . ', title='.db_input($title);
+        return db_query($sql);
+    }
+
+    function find($query, $criteria=array(), $model=false, $sort=array()) {
+        global $thisstaff;
+
+        $mode = ' IN BOOLEAN MODE';
+        #if (count(explode(' ', $query)) == 1)
+        #    $mode = ' WITH QUERY EXPANSION';
+        $search = 'MATCH (search.title, search.content) AGAINST ('
+            .db_input($query)
+            .$mode.')';
+        $tables = array();
+        $P = TABLE_PREFIX;
+        $sort = '';
+
+        if ($query) {
+            $tables[] = "(
+                SELECT object_type, object_id, $search AS `relevance`
+                FROM `{$P}_search` `search`
+                WHERE $search
+            ) `search`";
+            $sort = 'ORDER BY `search`.`relevance`';
+        }
+
+        switch ($model) {
+        case false:
+        case 'Ticket':
+            $tables[] = "(select ticket_id as ticket_id from {$P}ticket
+            ) B1 ON (B1.ticket_id = search.object_id and search.object_type = 'T')";
+            $tables[] = "(select A2.id as thread_id, A1.ticket_id from {$P}ticket A1
+                join {$P}ticket_thread A2 on (A1.ticket_id = A2.ticket_id)
+            ) B2 ON (B2.thread_id = search.object_id and search.object_type = 'H')";
+            $tables[] = "(select A3.id as user_id, A1.ticket_id from {$P}user A3
+                join {$P}ticket A1 on (A1.user_id = A3.id)
+            ) B3 ON (B3.user_id = search.object_id and search.object_type = 'U')";
+            $tables[] = "(select A4.id as org_id, A1.ticket_id from {$P}organization A4
+                join {$P}user A3 on (A3.org_id = A4.id) join {$P}ticket A1 on (A1.user_id = A3.id)
+            ) B4 ON (B4.org_id = search.object_id and search.object_type = 'O')";
+            $key = 'COALESCE(B1.ticket_id, B2.ticket_id, B3.ticket_id, B4.ticket_id)';
+            $tables[] = "{$P}ticket A1 ON (A1.ticket_id = {$key})";
+            $tables[] = "{$P}ticket_status A2 ON (A1.status_id = A2.id)";
+            $cdata_search = false;
+            $where = array();
+
+            if ($criteria) {
+                foreach ($criteria as $name=>$value) {
+                    switch ($name) {
+                    case 'status_id':
+                        $where[] = 'A2.id = '.db_input($value);
+                        break;
+                    case 'state':
+                        $where[] = 'A2.state = '.db_input($value);
+                        break;
+                    case 'state__in':
+                        $where[] = 'A2.state IN ('.implode(',',db_input($value)).')';
+                        break;
+                    case 'topic_id':
+                    case 'staff_id':
+                    case 'team_id':
+                    case 'dept_id':
+                    case 'user_id':
+                    case 'isanswered':
+                    case 'isoverdue':
+                    case 'number':
+                        $where[] = sprintf('A1.%s = %s', $name, db_input($value));
+                        break;
+                    case 'created__gte':
+                        $where[] = sprintf('A1.created >= %s', db_input($value));
+                        break;
+                    case 'created__lte':
+                        $where[] = sprintf('A1.created <= %s', db_input($value));
+                        break;
+                    case 'email':
+                    case 'org_id':
+                    case 'form_id':
+                    default:
+                        if (strpos($name, 'cdata.') === 0) {
+                            // Search ticket CDATA table
+                            $cdata_search = true;
+                            $name = substr($name, 6);
+                            if (is_array($value)) {
+                                $where[] = '(' . implode(' OR ', array_map(
+                                    function($k) use ($name) {
+                                        return sprintf('FIND_IN_SET(%s, cdata.`%s`)',
+                                            db_input($k), $name);
+                                    }, $value)
+                                ) . ')';
+                            }
+                            else {
+                                $where[] = sprintf("cdata.%s = %s", $name, db_input($value));
+                            }
+                        }
+                    }
+                }
+            }
+            if ($cdata_search)
+                $tables[] = TABLE_PREFIX.'ticket__cdata cdata'
+                    .' ON (cdata.ticket_id = A1.ticket_id)';
+
+            // Always consider the current staff's access
+            $thisstaff->getDepts();
+            $access = array();
+            $access[] = '(A1.staff_id=' . db_input($thisstaff->getId())
+                .' AND A2.state="open")';
+
+            if (!$thisstaff->showAssignedOnly() && ($depts=$thisstaff->getDepts()))
+                $access[] = 'A1.dept_id IN ('
+                    . ($depts ? implode(',', db_input($depts)) : 0)
+                    . ')';
+
+            if (($teams = $thisstaff->getTeams()) && count(array_filter($teams)))
+                $access[] = 'A1.team_id IN ('
+                    .implode(',', db_input(array_filter($teams)))
+                    .') AND A2.state="open"';
+
+            $where[] = '(' . implode(' OR ', $access) . ')';
+
+            // TODO: Consider sorting preferences
+
+            $sql = 'SELECT DISTINCT '
+                . $key
+                . ' FROM '
+                . implode(' LEFT JOIN ', $tables)
+                . ' WHERE ' . implode(' AND ', $where)
+                . $sort
+                . ' LIMIT 500';
+        }
+
+        $class = get_class();
+        $auto_create = function($db_error) use ($class) {
+
+            if ($db_error != 1146)
+                // Perform the standard error handling
+                return true;
+
+            // Create the search table automatically
+            $class::createSearchTable();
+        };
+        $res = db_query($sql, $auto_create);
+        $object_ids = array();
+
+        while ($row = db_fetch_row($res))
+            $object_ids[] = $row[0];
+
+        return $object_ids;
+    }
+
+    static function createSearchTable() {
+        $sql = 'CREATE TABLE IF NOT EXISTS '.TABLE_PREFIX.'_search (
+            `object_type` varchar(8) not null,
+            `object_id` int(11) unsigned not null,
+            `title` text collate utf8_general_ci,
+            `content` text collate utf8_general_ci,
+            primary key `object` (`object_type`, `object_id`),
+            fulltext key `search` (`title`, `content`)
+        ) ENGINE=MyISAM CHARSET=utf8';
+        return db_query($sql);
+    }
+
+    /**
+     * Cooperates with the cron system to automatically find content that is
+     * not index in the _search table and add it to the index.
+     */
+    function IndexOldStuff() {
+        $class = get_class();
+        $auto_create = function($db_error) use ($class) {
+
+            if ($db_error != 1146)
+                // Perform the standard error handling
+                return true;
+
+            // Create the search table automatically
+            $class::__init();
+
+        };
+
+        // THREADS ----------------------------------
+
+        $sql = "SELECT A1.`id`, A1.`title`, A1.`body`, A1.`format` FROM `".TICKET_THREAD_TABLE."` A1
+            LEFT JOIN `".TABLE_PREFIX."_search` A2 ON (A1.`id` = A2.`object_id` AND A2.`object_type`='H')
+            WHERE A2.`object_id` IS NULL AND (A1.poster <> 'SYSTEM')
+            AND (LENGTH(A1.`title`) + LENGTH(A1.`body`) > 0)
+            ORDER BY A1.`id` DESC";
+        if (!($res = db_query_unbuffered($sql, $auto_create)))
+            return false;
+
+        while ($row = db_fetch_row($res)) {
+            $body = ThreadBody::fromFormattedText($row[2], $row[3]);
+            $body = $body->getSearchable();
+            $title = Format::searchable($row[1]);
+            if (!$body && !$title)
+                continue;
+            $record = array('H', $row[0], $title, $body);
+            if (!$this->__index($record))
+                return;
+        }
+
+        // TICKETS ----------------------------------
+
+        $sql = "SELECT A1.`ticket_id` FROM `".TICKET_TABLE."` A1
+            LEFT JOIN `".TABLE_PREFIX."_search` A2 ON (A1.`ticket_id` = A2.`object_id` AND A2.`object_type`='T')
+            WHERE A2.`object_id` IS NULL
+            ORDER BY A1.`ticket_id` DESC";
+        if (!($res = db_query_unbuffered($sql, $auto_create)))
+            return false;
+
+        while ($row = db_fetch_row($res)) {
+            $ticket = Ticket::lookup($row[0]);
+            $cdata = $ticket->loadDynamicData();
+            $content = array();
+            foreach ($cdata as $k=>$a)
+                if ($k != 'subject' && ($v = $a->getSearchable()))
+                    $content[] = $v;
+            $record = array('T', $ticket->getId(),
+                Format::searchable($ticket->getNumber().' '.$ticket->getSubject()),
+                implode("\n", $content));
+            if (!$this->__index($record))
+                return;
+        }
+
+        // USERS ------------------------------------
+
+        $sql = "SELECT A1.`id` FROM `".USER_TABLE."` A1
+            LEFT JOIN `".TABLE_PREFIX."_search` A2 ON (A1.`id` = A2.`object_id` AND A2.`object_type`='U')
+            WHERE A2.`object_id` IS NULL
+            ORDER BY A1.`id` DESC";
+        if (!($res = db_query_unbuffered($sql, $auto_create)))
+            return false;
+
+        while ($row = db_fetch_row($res)) {
+            $user = User::lookup($row[0]);
+            $cdata = $user->getDynamicData();
+            $content = array();
+            foreach ($user->emails as $e)
+                $content[] = $e->address;
+            foreach ($cdata as $e)
+                foreach ($e->getAnswers() as $a)
+                    if ($c = $a->getSearchable())
+                        $content[] = $c;
+            $record = array('U', $user->getId(),
+                Format::searchable($user->getFullName()),
+                trim(implode("\n", $content)));
+            if (!$this->__index($record))
+                return;
+        }
+
+        // ORGANIZATIONS ----------------------------
+
+        $sql = "SELECT A1.`id` FROM `".ORGANIZATION_TABLE."` A1
+            LEFT JOIN `".TABLE_PREFIX."_search` A2 ON (A1.`id` = A2.`object_id` AND A2.`object_type`='O')
+            WHERE A2.`object_id` IS NULL
+            ORDER BY A1.`id` DESC";
+        if (!($res = db_query_unbuffered($sql, $auto_create)))
+            return false;
+
+        while ($row = db_fetch_row($res)) {
+            $org = Organization::lookup($row[0]);
+            $cdata = $org->getDynamicData();
+            $content = array();
+            foreach ($cdata as $e)
+                foreach ($e->getAnswers() as $a)
+                    if ($c = $a->getSearchable())
+                        $content[] = $c;
+            $record = array('O', $org->getId(),
+                Format::searchable($org->getName()),
+                trim(implode("\n", $content)));
+            if (!$this->__index($record))
+                return null;
+        }
+
+        // KNOWLEDGEBASE ----------------------------
+
+        require_once INCLUDE_DIR . 'class.faq.php';
+        $sql = "SELECT A1.`faq_id` FROM `".FAQ_TABLE."` A1
+            LEFT JOIN `".TABLE_PREFIX."_search` A2 ON (A1.`faq_id` = A2.`object_id` AND A2.`object_type`='K')
+            WHERE A2.`object_id` IS NULL
+            ORDER BY A1.`faq_id` DESC";
+        if (!($res = db_query_unbuffered($sql, $auto_create)))
+            return false;
+
+        while ($row = db_fetch_row($res)) {
+            $faq = FAQ::lookup($row[0]);
+            $q = $faq->getQuestion();
+            if ($k = $faq->getKeywords())
+                $q = $k.' '.$q;
+            $record = array('K', $faq->getId(),
+                Format::searchable($q),
+                $faq->getSearchableAnswer());
+            if (!$this->__index($record))
+                return;
+        }
+
+        // FILES ------------------------------------
+
+        // Flush non-full batch of records
+        $this->__index(null, true);
+    }
+
+    function __index($record, $force_flush=false) {
+        static $queue = array();
+
+        if ($record)
+            $queue[] = $record;
+        elseif (!$queue)
+            return;
+
+        if (!$force_flush && count($queue) < $this::$BATCH_SIZE)
+            return true;
+
+        foreach ($queue as &$r)
+            $r = sprintf('(%s)', implode(',', db_input($r)));
+        unset($r);
+
+        $sql = 'INSERT INTO `'.TABLE_PREFIX.'_search` (`object_type`, `object_id`, `title`, `content`)
+            VALUES '.implode(',', $queue);
+        if (!db_query($sql) || count($queue) != db_affected_rows())
+            throw new Exception('Unable to index content');
+
+        $queue = array();
+
+        if (!--$this->max_batches)
+            return null;
+
+        return true;
+    }
+
+    static function __init() {
+        self::createSearchTable();
+    }
+
+}
+
+Signal::connect('system.install',
+        array('MysqlSearchBackend', '__init'));
+
+MysqlSearchBackend::register();
diff --git a/include/class.sequence.php b/include/class.sequence.php
new file mode 100644
index 0000000000000000000000000000000000000000..1b3fc3182ec1e890d8517d3c2969818d862fb40a
--- /dev/null
+++ b/include/class.sequence.php
@@ -0,0 +1,229 @@
+<?php
+
+require_once INCLUDE_DIR . 'class.orm.php';
+
+class Sequence extends VerySimpleModel {
+
+    static $meta = array(
+        'table' => SEQUENCE_TABLE,
+        'pk' => array('id'),
+        'ordering' => array('name'),
+    );
+
+    const FLAG_INTERNAL = 0x0001;
+
+    /**
+     * Function: next
+     *
+     * Fetch the next number in the sequence. The next number in the
+     * sequence will be adjusted in the database so that subsequent calls to
+     * this function should never receive the same result.
+     *
+     * Optionally, a format specification can be sent to the function and
+     * the next sequence number will be returned padded. See the `::format`
+     * function for more details.
+     *
+     * Optionally, a check callback can be specified to ensure the next
+     * value of the sequence is valid. This might be useful for a
+     * pseudo-random generator which might repeat existing numbers. The
+     * callback should have the following signature and should return
+     * boolean TRUE to approve the number.
+     *
+     * Parameters:
+     * $format - (string) Format specification for the result
+     * $check - (function($format, $next)) Validation callback function
+     *      where $next will be the next value as an integer, and $formatted
+     *      will be the formatted version of the number, if a $format
+     *      parameter were passed to the `::next` method.
+     *
+     * Returns:
+     * (int|string) - next number in the sequence, optionally formatted and
+     * verified.
+     */
+    function next($format=false, $check=false) {
+        $digits = $format ? $this->getDigitCount($format) : false;
+
+        if ($check && !is_callable($check))
+            $check = false;
+
+        do {
+            $next = $this->__next($digits);
+            $formatted = $format ? $this->format($format, $next) : $next;
+        }
+        while ($check
+                && !call_user_func_array($check, array($formatted, $next)));
+
+        return $formatted;
+    }
+
+    /**
+     * Function: current
+     *
+     * Peeks at the next number in the sequence without incrementing the
+     * sequence.
+     *
+     * Parameters:
+     * $format - (string:optional) format string to receive the current
+     *      sequence number
+     *
+     * Returns:
+     * (int|string) - the next number in the sequence without advancing the
+     * sequence, optionally formatted. See the `::format` method for
+     * formatting details.
+     */
+     function current($format=false) {
+        return $format ? $this->format($format, $this->next) : $this->next;
+    }
+
+    /**
+     * Function: format
+     *
+     * Formats a number to the given format. The number will be placed into
+     * the format string according to the locations of hash characters (#)
+     * in the string. If more hash characters are encountered than digits
+     * the digits are left-padded accoring to the sequence padding
+     * character. If fewer are found, the last group will receive all the
+     * remaining digits.
+     *
+     * Hash characters can be escaped with a backslash (\#) and will emit a
+     * single hash character to the output.
+     *
+     * Parameters:
+     * $format - (string) Format string for the number, e.g. "TX-######-US"
+     * $number - (int) Number to appear in the format. If not
+     *      specified the next number in this sequence will be used.
+     */
+    function format($format, $number) {
+        $groups = array();
+        preg_match_all('/(?<!\\\)#+/', $format, $groups, PREG_OFFSET_CAPTURE);
+
+        $total = 0;
+        foreach ($groups[0] as $g)
+            $total += strlen($g[0]);
+
+        $number = str_pad($number, $total, $this->padding, STR_PAD_LEFT);
+        $output = '';
+        $start = $noff = 0;
+        // Interate through the ### groups and replace the number of hash
+        // marks with numbers from the sequence
+        foreach ($groups[0] as $g) {
+            $size = strlen($g[0]);
+            // Add format string from previous marker to current ## group
+            $output .= str_replace('\#', '#',
+                substr($format, $start, $g[1] - $start));
+            // Add digits from the sequence number
+            $output .= substr($number, $noff, $size);
+            // Set offset counts for the next loop
+            $start = $g[1] + $size;
+            $noff += $size;
+        }
+        // If there are more digits of number than # marks, add the number
+        // where the last hash mark was found
+        if (strlen($number) > $noff)
+            $output .= substr($number, $noff);
+        // Add format string from ending ## group
+        $output .= str_replace('\#', '#', substr($format, $start));
+        return $output;
+    }
+
+    function getDigitCount($format) {
+        $total = 0;
+        $groups = array();
+
+        return preg_match_all('/(?<!\\\)#/', $format, $groups);
+    }
+
+    /**
+     * Function: __next
+     *
+     * Internal implementation of the next number generator. This method
+     * will lock the database object backing to protect against concurent
+     * ticket processing. The lock will be released at the conclusion of the
+     * session.
+     *
+     * Parameters:
+     * $digits - (int:optional) number of digits (size) of the number. This
+     *      is useful for random sequences which need a size hint to
+     *      generate a "next" value.
+     *
+     * Returns:
+     * (int) - The current number in the sequence. The sequence is advanced
+     * and assured to be session-wise atomic before the value is returned.
+     */
+    function __next($digits=false) {
+        // Lock the database object -- this is important to handle concurrent
+        // requests for new numbers
+        static::objects()->filter(array('id'=>$this->id))->lock()->one();
+
+        // Increment the counter
+        $next = $this->next;
+        $this->next += $this->increment;
+        $this->updated = SqlFunction::NOW();
+        $this->save();
+
+        return $next;
+    }
+
+    function hasFlag($flag) {
+        return $this->flags & $flag != 0;
+    }
+    function setFlag($flag, $value=true) {
+        if ($value)
+            $this->flags |= $flag;
+        else
+            $this->flags &= ~$flag;
+    }
+
+    function getName() {
+        return $this->name;
+    }
+
+    function isValid() {
+        if (!$this->name)
+            return 'Name is required';
+        if (!$this->increment)
+            return 'Non-zero increment is required';
+        if (!$this->next || $this->next < 0)
+            return 'Positive "next" value is required';
+
+        if (!$this->padding)
+            $this->padding = '0';
+
+        return true;
+    }
+
+    function __get($what) {
+        // Pseudo-property for $sequence->current
+        if ($what == 'current')
+            return $this->current();
+        return parent::__get($what);
+    }
+
+    function __create($data) {
+        $instance = parent::create($data);
+        $instance->save();
+        return $instance;
+    }
+}
+
+class RandomSequence extends Sequence {
+    var $padding = '0';
+
+    // Override the ORM constructor and do nothing
+    function __construct() {}
+
+    function __next($digits=6) {
+        if ($digits < 6)
+            $digits = 6;
+
+        return Misc::randNumber($digits);
+    }
+
+    function current($format=false) {
+        return $this->next($format);
+    }
+
+    function save() {
+        throw new RuntimeException('RandomSequence is not database-backed');
+    }
+}
diff --git a/include/class.setup.php b/include/class.setup.php
index cc13fe2f5e60b273f4aff26dbcb754aebfd917c0..fe70fc10b10f8688e9eabbeab0944aa27e3379f5 100644
--- a/include/class.setup.php
+++ b/include/class.setup.php
@@ -30,14 +30,15 @@ Class SetupWizard {
 
     function SetupWizard(){
         $this->errors=array();
-        $this->version_verbose = ('osTicket '. strtoupper(THIS_VERSION));
+        $this->version_verbose = sprintf(__('osTicket %s' /* <%s> is for the version */),
+            THIS_VERSION);
 
     }
 
     function load_sql_file($file, $prefix, $abort=true, $debug=false) {
 
         if(!file_exists($file) || !($schema=file_get_contents($file)))
-            return $this->abort('Error accessing SQL file '.basename($file), $debug);
+            return $this->abort(sprintf(__('Error accessing SQL file %s'),basename($file)), $debug);
 
         return $this->load_sql($schema, $prefix, $abort, $debug);
     }
@@ -46,7 +47,6 @@ Class SetupWizard {
         load SQL schema - assumes MySQL && existing connection
         */
     function load_sql($schema, $prefix, $abort=true, $debug=false) {
-
         # Strip comments and remarks
         $schema=preg_replace('%^\s*(#|--).*$%m', '', $schema);
         # Replace table prefix
@@ -55,7 +55,7 @@ Class SetupWizard {
         if(!($statements = array_filter(array_map('trim',
                 // Thanks, http://stackoverflow.com/a/3147901
                 preg_split("/;(?=(?:[^']*'[^']*')*[^']*$)/", $schema)))))
-            return $this->abort('Error parsing SQL schema', $debug);
+            return $this->abort(__('Error parsing SQL schema'), $debug);
 
 
         db_query('SET SESSION SQL_MODE =""', false);
diff --git a/include/class.signal.php b/include/class.signal.php
index af257cd4a2dd560076a33d5a34d31d02750c9a01..a98d4cf63a39979382bd668d857994db0afbeb12 100644
--- a/include/class.signal.php
+++ b/include/class.signal.php
@@ -58,9 +58,9 @@ class Signal {
         if (!isset(self::$subscribers[$signal])) self::$subscribers[$signal] = array();
         // XXX: Ensure $object if set is a class
         if ($object && !is_string($object))
-            trigger_error("Invalid object: $object: Expected class");
+            trigger_error(sprintf(_S("Invalid object: %s: Expected class"), $object));
         elseif ($check && !is_callable($check)) {
-            trigger_error("Invalid check function: Must be callable");
+            trigger_error(_S("Invalid check function: Must be callable"));
             $check = null;
         }
         self::$subscribers[$signal][] = array($object, $callable, $check);
diff --git a/include/class.sla.php b/include/class.sla.php
index 1bd84e5d246f4caaf7b864147cc038ad39ced6a5..070743a2bb6f0c8e22c3664be229c8817a401852 100644
--- a/include/class.sla.php
+++ b/include/class.sla.php
@@ -13,6 +13,7 @@
 
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
+
 class SLA {
 
     var $id;
@@ -132,13 +133,15 @@ class SLA {
     function getSLAs() {
 
         $slas=array();
+
         $sql='SELECT id, name, isactive, grace_period FROM '.SLA_TABLE.' ORDER BY name';
         if(($res=db_query($sql)) && db_num_rows($res)) {
             while($row=db_fetch_array($res))
-                $slas[$row['id']] = sprintf('%s (%d hrs - %s)',
+                $slas[$row['id']] = sprintf(__('%s (%d hours - %s)'
+                        /* Tokens are <name> (<#> hours - <Active|Disabled>) */),
                         $row['name'],
                         $row['grace_period'],
-                        $row['isactive']?'Active':'Disabled');
+                        $row['isactive']?__('Active'):__('Disabled'));
         }
 
         return $slas;
@@ -160,16 +163,15 @@ class SLA {
 
     function save($id,$vars,&$errors) {
 
-
         if(!$vars['grace_period'])
-            $errors['grace_period']='Grace period required';
+            $errors['grace_period']=__('Grace period required');
         elseif(!is_numeric($vars['grace_period']))
-            $errors['grace_period']='Numeric value required (in hours)';
+            $errors['grace_period']=__('Numeric value required (in hours)');
 
         if(!$vars['name'])
-            $errors['name']='Name required';
+            $errors['name']=__('Name is required');
         elseif(($sid=SLA::getIdByName($vars['name'])) && $sid!=$id)
-            $errors['name']='Name already exists';
+            $errors['name']=__('Name already exists');
 
         if($errors) return false;
 
@@ -186,7 +188,8 @@ class SLA {
             if(db_query($sql))
                 return true;
 
-            $errors['err']='Unable to update SLA. Internal error occurred';
+            $errors['err']=sprintf(__('Unable to update %s.'), __('this SLA plan'))
+               .' '.__('Internal error occurred');
         }else{
             if (isset($vars['id']))
                 $sql .= ', id='.db_input($vars['id']);
@@ -195,7 +198,8 @@ class SLA {
             if(db_query($sql) && ($id=db_insert_id()))
                 return $id;
 
-            $errors['err']='Unable to add SLA. Internal error';
+            $errors['err']=sprintf(__('Unable to add %s.'), __('this SLA plan'))
+               .' '.__('Internal error occurred');
         }
 
         return false;
diff --git a/include/class.staff.php b/include/class.staff.php
index aab9a737b991ecefa6e05597edb16199afe261a4..da2473c0e75d45d05d9d6cf52e05f16c602f3164 100644
--- a/include/class.staff.php
+++ b/include/class.staff.php
@@ -442,64 +442,65 @@ class Staff extends AuthenticatedUser {
         $vars['lastname']=Format::striptags($vars['lastname']);
 
         if($this->getId()!=$vars['id'])
-            $errors['err']='Internal Error';
+            $errors['err']=__('Internal error occurred');
 
         if(!$vars['firstname'])
-            $errors['firstname']='First name required';
+            $errors['firstname']=__('First name is required');
 
         if(!$vars['lastname'])
-            $errors['lastname']='Last name required';
+            $errors['lastname']=__('Last name is required');
 
         if(!$vars['email'] || !Validator::is_email($vars['email']))
-            $errors['email']='Valid email required';
+            $errors['email']=__('Valid email is required');
         elseif(Email::getIdByEmail($vars['email']))
-            $errors['email']='Already in-use as system email';
+            $errors['email']=__('Already in-use as system email');
         elseif(($uid=Staff::getIdByEmail($vars['email'])) && $uid!=$this->getId())
-            $errors['email']='Email already in-use by another staff member';
+            $errors['email']=__('Email already in-use by another agent');
 
         if($vars['phone'] && !Validator::is_phone($vars['phone']))
-            $errors['phone']='Valid number required';
+            $errors['phone']=__('Valid phone number is required');
 
         if($vars['mobile'] && !Validator::is_phone($vars['mobile']))
-            $errors['mobile']='Valid number required';
+            $errors['mobile']=__('Valid phone number is required');
 
         if($vars['passwd1'] || $vars['passwd2'] || $vars['cpasswd']) {
 
             if(!$vars['passwd1'])
-                $errors['passwd1']='New password required';
+                $errors['passwd1']=__('New password is required');
             elseif($vars['passwd1'] && strlen($vars['passwd1'])<6)
-                $errors['passwd1']='Must be at least 6 characters';
+                $errors['passwd1']=__('Password must be at least 6 characters');
             elseif($vars['passwd1'] && strcmp($vars['passwd1'], $vars['passwd2']))
-                $errors['passwd2']='Password(s) do not match';
+                $errors['passwd2']=__('Passwords do not match');
 
             if (($rtoken = $_SESSION['_staff']['reset-token'])) {
                 $_config = new Config('pwreset');
                 if ($_config->get($rtoken) != $this->getId())
                     $errors['err'] =
-                        'Invalid reset token. Logout and try again';
+                        __('Invalid reset token. Logout and try again');
                 elseif (!($ts = $_config->lastModified($rtoken))
                         && ($cfg->getPwResetWindow() < (time() - strtotime($ts))))
                     $errors['err'] =
-                        'Invalid reset token. Logout and try again';
+                        __('Invalid reset token. Logout and try again');
             }
             elseif(!$vars['cpasswd'])
-                $errors['cpasswd']='Current password required';
+                $errors['cpasswd']=__('Current password is required');
             elseif(!$this->cmp_passwd($vars['cpasswd']))
-                $errors['cpasswd']='Invalid current password!';
+                $errors['cpasswd']=__('Invalid current password!');
             elseif(!strcasecmp($vars['passwd1'], $vars['cpasswd']))
-                $errors['passwd1']='New password MUST be different from the current password!';
+                $errors['passwd1']=__('New password MUST be different from the current password!');
         }
 
         if(!$vars['timezone_id'])
-            $errors['timezone_id']='Time zone required';
+            $errors['timezone_id']=__('Time zone selection is required');
 
         if($vars['default_signature_type']=='mine' && !$vars['signature'])
-            $errors['default_signature_type'] = "You don't have a signature";
+            $errors['default_signature_type'] = __("You don't have a signature");
 
         if($errors) return false;
 
         $this->config->set('lang', $vars['lang']);
         $_SESSION['staff:lang'] = null;
+        TextDomain::configureForUser($this);
 
         $sql='UPDATE '.STAFF_TABLE.' SET updated=NOW() '
             .' ,firstname='.db_input($vars['firstname'])
@@ -641,86 +642,6 @@ class Staff extends AuthenticatedUser {
         return ($id && ($staff= new Staff($id)) && $staff->getId()) ? $staff : null;
     }
 
-    function login($username, $passwd, &$errors, $strike=true) {
-        global $ost, $cfg;
-
-
-        if($_SESSION['_staff']['laststrike']) {
-            if((time()-$_SESSION['_staff']['laststrike'])<$cfg->getStaffLoginTimeout()) {
-                $errors['err']='Max. failed login attempts reached';
-                $_SESSION['_staff']['laststrike'] = time(); //reset timer.
-            } else { //Timeout is over.
-                //Reset the counter for next round of attempts after the timeout.
-                $_SESSION['_staff']['laststrike']=null;
-                $_SESSION['_staff']['strikes']=0;
-            }
-        }
-
-        if(!$username || !$passwd || is_numeric($username))
-            $errors['err'] = 'Username and password required';
-
-        if($errors) return false;
-
-        if(($user=new StaffSession(trim($username))) && $user->getId() && $user->check_passwd($passwd)) {
-            self::_do_login($user, $username);
-
-            Signal::send('auth.login.succeeded', $user);
-            $user->cancelResetTokens();
-
-            return $user;
-        }
-
-        $info = array('username'=>$username, 'password'=>$passwd);
-        Signal::send('auth.login.failed', null, $info);
-
-        //If we get to this point we know the login failed.
-        $_SESSION['_staff']['strikes']+=1;
-        if(!$errors && $_SESSION['_staff']['strikes']>$cfg->getStaffMaxLogins()) {
-            $errors['err']='Forgot your login info? Contact Admin.';
-            $_SESSION['_staff']['laststrike']=time();
-            $alert='Excessive login attempts by a staff member?'."\n".
-                   'Username: '.$username."\n".'IP: '.$_SERVER['REMOTE_ADDR']."\n".'TIME: '.date('M j, Y, g:i a T')."\n\n".
-                   'Attempts #'.$_SESSION['_staff']['strikes']."\n".'Timeout: '.($cfg->getStaffLoginTimeout()/60)." minutes \n\n";
-            $ost->logWarning('Excessive login attempts ('.$username.')', $alert, ($cfg->alertONLoginError()));
-
-        } elseif($_SESSION['_staff']['strikes']%2==0) { //Log every other failed login attempt as a warning.
-            $alert='Username: '.$username."\n".'IP: '.$_SERVER['REMOTE_ADDR'].
-                   "\n".'TIME: '.date('M j, Y, g:i a T')."\n\n".'Attempts #'.$_SESSION['_staff']['strikes'];
-            $ost->logWarning('Failed staff login attempt ('.$username.')', $alert, false);
-        }
-
-        return false;
-    }
-
-    function _do_login($user, $username) {
-        global $ost;
-
-        //update last login && password reset stuff.
-        $sql='UPDATE '.STAFF_TABLE.' SET lastlogin=NOW() ';
-        if($user->isPasswdResetDue() && !$user->isAdmin())
-            $sql.=',change_passwd=1';
-        $sql.=' WHERE staff_id='.db_input($user->getId());
-        db_query($sql);
-        //Now set session crap and lets roll baby!
-        $_SESSION['_staff'] = array(); //clear.
-        $_SESSION['_staff']['userID'] = $user->getId();
-        $user->refreshSession(true); //set the hash.
-        $_SESSION['TZ_OFFSET'] = $user->getTZoffset();
-        $_SESSION['TZ_DST'] = $user->observeDaylight();
-
-        //Log debug info.
-        $ost->logDebug('Staff login',
-                sprintf("%s logged in [%s]", $user->getUserName(), $_SERVER['REMOTE_ADDR'])); //Debug.
-
-        //Regenerate session id.
-        $sid=session_id(); //Current id
-        session_regenerate_id(TRUE);
-        //Destroy old session ID - needed for PHP version < 5.1.0 TODO: remove when we move to php 5.3 as min. requirement.
-        if(($session=$ost->getSession()) && is_object($session) && $sid!=session_id())
-            $session->destroy($sid);
-
-        return $user;
-    }
 
     function create($vars, &$errors) {
         if(($id=self::save(0, $vars, $errors)) && ($staff=Staff::lookup($id))) {
@@ -750,7 +671,7 @@ class Staff extends AuthenticatedUser {
         $token = Misc::randCode(48); // 290-bits
 
         if (!$content)
-            return new Error('Unable to retrieve password reset email template');
+            return new Error(/* @trans */ 'Unable to retrieve password reset email template');
 
         $vars = array(
             'url' => $ost->getConfig()->getBaseUrl(),
@@ -771,12 +692,12 @@ class Staff extends AuthenticatedUser {
         Signal::send('auth.pwreset.email', $this, $info);
 
         if ($info['log'])
-            $ost->logWarning('Staff Password Reset', sprintf(
-               'Password reset was attempted for staff member: %s<br><br>
-                Requested-User-Id: %s<br>
-                Source-Ip: %s<br>
-                Email-Sent-To: %s<br>
-                Email-Sent-Via: %s',
+            $ost->logWarning(_S('Agent Password Reset'), sprintf(
+             _S('Password reset was attempted for agent: %1$s<br><br>
+                Requested-User-Id: %2$s<br>
+                Source-Ip: %3$s<br>
+                Email-Sent-To: %4$s<br>
+                Email-Sent-Via: %5$s'),
                 $this->getName(),
                 $_POST['userid'],
                 $_SERVER['REMOTE_ADDR'],
@@ -803,55 +724,55 @@ class Staff extends AuthenticatedUser {
         $vars['lastname']=Format::striptags($vars['lastname']);
 
         if($id && $id!=$vars['id'])
-            $errors['err']='Internal Error';
+            $errors['err']=__('Internal Error');
 
         if(!$vars['firstname'])
-            $errors['firstname']='First name required';
+            $errors['firstname']=__('First name required');
         if(!$vars['lastname'])
-            $errors['lastname']='Last name required';
+            $errors['lastname']=__('Last name required');
 
         $error = '';
         if(!$vars['username'] || !Validator::is_username($vars['username'], $error))
-            $errors['username']=($error) ? $error : 'Username required';
+            $errors['username']=($error) ? $error : __('Username is required');
         elseif(($uid=Staff::getIdByUsername($vars['username'])) && $uid!=$id)
-            $errors['username']='Username already in use';
+            $errors['username']=__('Username already in use');
 
         if(!$vars['email'] || !Validator::is_email($vars['email']))
-            $errors['email']='Valid email required';
+            $errors['email']=__('Valid email is required');
         elseif(Email::getIdByEmail($vars['email']))
-            $errors['email']='Already in-use system email';
+            $errors['email']=__('Already in use system email');
         elseif(($uid=Staff::getIdByEmail($vars['email'])) && $uid!=$id)
-            $errors['email']='Email already in use by another staff member';
+            $errors['email']=__('Email already in use by another agent');
 
         if($vars['phone'] && !Validator::is_phone($vars['phone']))
-            $errors['phone']='Valid number required';
+            $errors['phone']=__('Valid phone number is required');
 
         if($vars['mobile'] && !Validator::is_phone($vars['mobile']))
-            $errors['mobile']='Valid number required';
+            $errors['mobile']=__('Valid phone number is required');
 
         if($vars['passwd1'] || $vars['passwd2'] || !$id) {
             if($vars['passwd1'] && strcmp($vars['passwd1'], $vars['passwd2'])) {
-                $errors['passwd2']='Password(s) do not match';
+                $errors['passwd2']=__('Passwords do not match');
             }
             elseif ($vars['backend'] != 'local' || $vars['welcome_email']) {
                 // Password can be omitted
             }
             elseif(!$vars['passwd1'] && !$id) {
-                $errors['passwd1']='Temp. password required';
-                $errors['temppasswd']='Required';
+                $errors['passwd1']=__('Temporary password is required');
+                $errors['temppasswd']=__('Required');
             } elseif($vars['passwd1'] && strlen($vars['passwd1'])<6) {
-                $errors['passwd1']='Must be at least 6 characters';
+                $errors['passwd1']=__('Password must be at least 6 characters');
             }
         }
 
         if(!$vars['dept_id'])
-            $errors['dept_id']='Department required';
+            $errors['dept_id']=__('Department is required');
 
         if(!$vars['group_id'])
-            $errors['group_id']='Group required';
+            $errors['group_id']=__('Group is required');
 
         if(!$vars['timezone_id'])
-            $errors['timezone_id']='Time zone required';
+            $errors['timezone_id']=__('Time zone selection is required');
 
         if($errors) return false;
 
@@ -891,13 +812,15 @@ class Staff extends AuthenticatedUser {
             if(db_query($sql) && db_affected_rows())
                 return true;
 
-            $errors['err']='Unable to update the user. Internal error occurred';
+            $errors['err']=sprintf(__('Unable to update %s.'), __('this agent'))
+               .' '.__('Internal error occurred');
         } else {
             $sql='INSERT INTO '.STAFF_TABLE.' '.$sql.', created=NOW()';
             if(db_query($sql) && ($uid=db_insert_id()))
                 return $uid;
 
-            $errors['err']='Unable to create user. Internal error';
+            $errors['err']=sprintf(__('Unable to create %s.'), __('this agent'))
+               .' '.__('Internal error occurred');
         }
 
         return false;
diff --git a/include/class.team.php b/include/class.team.php
index 21280bd0efdc19152a7f3b95be15d394797a8c00..11670c8537b843e3223c182ebc42d74dd81c574e 100644
--- a/include/class.team.php
+++ b/include/class.team.php
@@ -30,7 +30,7 @@ class Team {
 
         if(!$id && !($id=$this->getId()))
             return false;
-        
+
         $sql='SELECT team.*,count(member.staff_id) as members '
             .' FROM '.TEAM_TABLE.' team '
             .' LEFT JOIN '.TEAM_MEMBER_TABLE.' member USING(team_id) '
@@ -133,7 +133,7 @@ class Team {
     function update($vars, &$errors) {
 
         //reset team lead if they're being deleted
-        if($this->getLeadId()==$vars['lead_id'] 
+        if($this->getLeadId()==$vars['lead_id']
                 && $vars['remove'] && in_array($this->getLeadId(), $vars['remove']))
             $vars['lead_id']=0;
 
@@ -197,7 +197,7 @@ class Team {
     }
 
     function getTeams( $availableOnly=false ) {
-        
+
         $teams=array();
         $sql='SELECT team_id, name FROM '.TEAM_TABLE;
         if($availableOnly) {
@@ -224,23 +224,22 @@ class Team {
         return self::getTeams(true);
     }
 
-    function create($vars, &$errors) { 
+    function create($vars, &$errors) {
         return self::save(0, $vars, $errors);
     }
 
     function save($id, $vars, &$errors) {
-
         if($id && $vars['id']!=$id)
-            $errors['err']='Missing or invalid team';
-            
+            $errors['err']=__('Missing or invalid team');
+
         if(!$vars['name']) {
-            $errors['name']='Team name required';
+            $errors['name']=__('Team name is required');
         } elseif(strlen($vars['name'])<3) {
-            $errors['name']='Team name must be at least 3 chars.';
+            $errors['name']=__('Team name must be at least 3 chars.');
         } elseif(($tid=Team::getIdByName($vars['name'])) && $tid!=$id) {
-            $errors['name']='Team name already exists';
+            $errors['name']=__('Team name already exists');
         }
-        
+
         if($errors) return false;
 
         $sql='SET updated=NOW(),isenabled='.db_input($vars['isenabled']).
@@ -252,16 +251,18 @@ class Team {
             $sql='UPDATE '.TEAM_TABLE.' '.$sql.',lead_id='.db_input($vars['lead_id']).' WHERE team_id='.db_input($id);
             if(db_query($sql) && db_affected_rows())
                 return true;
-                    
-            $errors['err']='Unable to update the team. Internal error';
+
+            $errors['err']=sprintf(__('Unable to update %s.'), __('this team'))
+               .' '.__('Internal error occurred');
         } else {
             $sql='INSERT INTO '.TEAM_TABLE.' '.$sql.',created=NOW()';
             if(db_query($sql) && ($id=db_insert_id()))
                 return $id;
-                
-            $errors['err']='Unable to create the team. Internal error';
+
+            $errors['err']=sprintf(__('Unable to create %s.'), __('this team'))
+               .' '.__('Internal error occurred');
         }
-        
+
         return false;
     }
 }
diff --git a/include/class.template.php b/include/class.template.php
index d73640cf67f250a00e7dcdce08b7985563a148c2..d8e2b8a5d61af30a15e9ad1347772dc417b53695 100644
--- a/include/class.template.php
+++ b/include/class.template.php
@@ -22,63 +22,63 @@ class EmailTemplateGroup {
     var $ht;
     var $_templates;
     static $all_groups = array(
-        'sys' => 'System Management Templates',
-        'ticket.user' => 'End-User Ticket Templates',
-        'ticket.staff' => 'Staff Ticket Templates',
+        'sys' => /* @trans */ 'System Management Templates',
+        'ticket.user' => /* @trans */ 'End-User Email Templates',
+        'ticket.staff' => /* @trans */ 'Agent Email Templates',
     );
     static $all_names=array(
         'ticket.autoresp'=>array(
             'group'=>'ticket.user',
-            'name'=>'New Ticket Auto-response',
-            'desc'=>'Autoresponse sent to user, if enabled, on new ticket.'),
+            'name'=>/* @trans */ 'New Ticket Auto-response',
+            'desc'=>/* @trans */ 'Autoresponse sent to user, if enabled, on new ticket.'),
         'ticket.autoreply'=>array(
             'group'=>'ticket.user',
-            'name'=>'New Ticket Auto-reply',
-            'desc'=>'Canned Auto-reply sent to user on new ticket, based on filter matches. Overwrites "normal" auto-response.'),
+            'name'=>/* @trans */ 'New Ticket Auto-reply',
+            'desc'=>/* @trans */ 'Canned Auto-reply sent to user on new ticket, based on filter matches. Overwrites "normal" auto-response.'),
         'message.autoresp'=>array(
             'group'=>'ticket.user',
-            'name'=>'New Message Auto-response',
-            'desc'=>'Confirmation sent to user when a new message is appended to an existing ticket.'),
+            'name'=>/* @trans */ 'New Message Auto-response',
+            'desc'=>/* @trans */ 'Confirmation sent to user when a new message is appended to an existing ticket.'),
         'ticket.notice'=>array(
             'group'=>'ticket.user',
-            'name'=>'New Ticket Notice',
-            'desc'=>'Notice sent to user, if enabled, on new ticket created by staff on their behalf (e.g phone calls).'),
+            'name'=>/* @trans */ 'New Ticket Notice',
+            'desc'=>/* @trans */ 'Notice sent to user, if enabled, on new ticket created by an agent on their behalf (e.g phone calls).'),
         'ticket.overlimit'=>array(
             'group'=>'ticket.user',
-            'name'=>'Over Limit Notice',
-            'desc'=>'A one-time notice sent, if enabled, when user has reached the maximum allowed open tickets.'),
+            'name'=>/* @trans */ 'Over Limit Notice',
+            'desc'=>/* @trans */ 'A one-time notice sent, if enabled, when user has reached the maximum allowed open tickets.'),
         'ticket.reply'=>array(
             'group'=>'ticket.user',
-            'name'=>'Response/Reply Template',
-            'desc'=>'Template used on ticket response/reply'),
+            'name'=>/* @trans */ 'Response/Reply Template',
+            'desc'=>/* @trans */ 'Template used on ticket response/reply'),
         'ticket.activity.notice'=>array(
             'group'=>'ticket.user',
-            'name'=>'New Activity Notice',
-            'desc'=>'Template used to notify collaborators on ticket activity (e.g CC on reply)'),
+            'name'=>/* @trans */ 'New Activity Notice',
+            'desc'=>/* @trans */ 'Template used to notify collaborators on ticket activity (e.g CC on reply)'),
         'ticket.alert'=>array(
             'group'=>'ticket.staff',
-            'name'=>'New Ticket Alert',
-            'desc'=>'Alert sent to staff, if enabled, on new ticket.'),
+            'name'=>/* @trans */ 'New Ticket Alert',
+            'desc'=>/* @trans */ 'Alert sent to agents, if enabled, on new ticket.'),
         'message.alert'=>array(
             'group'=>'ticket.staff',
-            'name'=>'New Message Alert',
-            'desc'=>'Alert sent to staff, if enabled, when user replies to an existing ticket.'),
+            'name'=>/* @trans */ 'New Message Alert',
+            'desc'=>/* @trans */ 'Alert sent to agents, if enabled, when user replies to an existing ticket.'),
         'note.alert'=>array(
             'group'=>'ticket.staff',
-            'name'=>'Internal Note Alert',
-            'desc'=>'Alert sent to selected staff, if enabled, on new internal note.'),
+            'name'=>/* @trans */ 'Internal Note Alert',
+            'desc'=>/* @trans */ 'Alert sent to selected agents, if enabled, on new internal note.'),
         'assigned.alert'=>array(
             'group'=>'ticket.staff',
-            'name'=>'Ticket Assignment Alert',
-            'desc'=>'Alert sent to staff on ticket assignment.'),
+            'name'=>/* @trans */ 'Ticket Assignment Alert',
+            'desc'=>/* @trans */ 'Alert sent to agents on ticket assignment.'),
         'transfer.alert'=>array(
             'group'=>'ticket.staff',
-            'name'=>'Ticket Transfer Alert',
-            'desc'=>'Alert sent to staff on ticket transfer.'),
+            'name'=>/* @trans */ 'Ticket Transfer Alert',
+            'desc'=>/* @trans */ 'Alert sent to agents on ticket transfer.'),
         'ticket.overdue'=>array(
             'group'=>'ticket.staff',
-            'name'=>'Overdue Ticket Alert',
-            'desc'=>'Alert sent to staff on stale or overdue tickets.'),
+            'name'=>/* @trans */ 'Overdue Ticket Alert',
+            'desc'=>/* @trans */ 'Alert sent to agents on stale or overdue tickets.'),
         );
 
     function EmailTemplateGroup($id){
@@ -170,7 +170,8 @@ class EmailTemplateGroup {
         if ($tpl=EmailTemplate::fromInitialData($name, $this))
             return $tpl;
 
-        $ost->logWarning('Template Fetch Error', "Unable to fetch '$name' template - id #".$this->getId());
+        $ost->logWarning(_S('Template Fetch Error'),
+            sprintf(_S('Unable to fetch "%1$s" template - id #%d'), $name, $this->getId()));
         return false;
     }
 
@@ -248,9 +249,8 @@ class EmailTemplateGroup {
     }
 
     function update($vars,&$errors) {
-
         if(!$vars['isactive'] && $this->isInUse())
-            $errors['isactive']='Template in use cannot be disabled!';
+            $errors['isactive']=__('In-use template set cannot be disabled!');
 
         if(!$this->save($this->getId(),$vars,$errors))
             return false;
@@ -317,15 +317,15 @@ class EmailTemplateGroup {
         $vars['name']=Format::striptags(trim($vars['name']));
 
         if($id && $id!=$vars['tpl_id'])
-            $errors['err']='Internal error. Try again';
+            $errors['err']=__('Internal error occurred');
 
         if(!$vars['name'])
-            $errors['name']='Name required';
+            $errors['name']=__('Name is required');
         elseif(($tid=EmailTemplateGroup::getIdByName($vars['name'])) && $tid!=$id)
-            $errors['name']='Template name already exists';
+            $errors['name']=__('Template name already exists');
 
         if(!$id && ($vars['tpl_id'] && !($tpl=EmailTemplateGroup::lookup($vars['tpl_id']))))
-            $errors['tpl_id']='Invalid template group specified';
+            $errors['tpl_id']=__('Invalid template set specified');
 
         if($errors) return false;
 
@@ -343,7 +343,8 @@ class EmailTemplateGroup {
             if(db_query($sql))
                 return true;
 
-            $errors['err']='Unable to update the template. Internal error occurred';
+            $errors['err']=sprintf(__('Unable to update %s.'), __('this template set'))
+               .' '.__('Internal error occurred');
 
         } else {
 
@@ -352,7 +353,8 @@ class EmailTemplateGroup {
             $sql='INSERT INTO '.EMAIL_TEMPLATE_GRP_TABLE
                 .' SET created=NOW(), '.$sql;
             if(!db_query($sql) || !($new_id=db_insert_id())) {
-                $errors['err']='Unable to create template. Internal error';
+                $errors['err']=sprintf(__('Unable to create %s.'), __('this template set'))
+                   .' '.__('Internal error occurred');
                 return false;
             }
 
@@ -474,16 +476,16 @@ class EmailTemplate {
 
     function save($id, $vars, &$errors) {
         if(!$vars['subject'])
-            $errors['subject']='Message subject required';
+            $errors['subject']='Message subject is required';
 
         if(!$vars['body'])
-            $errors['body']='Message body required';
+            $errors['body']='Message body is required';
 
         if (!$id) {
             if (!$vars['tpl_id'])
-                $errors['tpl_id']='Template group required';
+                $errors['tpl_id']='Template set is required';
             if (!$vars['code_name'])
-                $errors['code_name']='Code name required';
+                $errors['code_name']='Code name is required';
         }
 
         if ($errors)
@@ -555,7 +557,7 @@ class EmailTemplate {
             return $templ;
         }
         raise_error("$lang/templates/$name.yaml: "
-            . 'Email templates must define both "subject" and "body" parts of the template',
+            . _S('Email templates must define both "subject" and "body" parts of the template'),
             'InitialDataError');
         return false;
     }
diff --git a/include/class.thread.php b/include/class.thread.php
index 87b0789c1aab0ec9ce37242252926781f179d1e6..c009ed2fbe4c4323a1138b0f6cdb2ac665a62aa0 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -482,7 +482,7 @@ Class ThreadEntry {
                 $uploaded[]=$id;
             else {
                 if(!$file['error'])
-                    $error = 'Unable to upload file - '.$file['name'];
+                    $error = sprintf(__('Unable to upload file - %s'),$file['name']);
                 elseif(is_numeric($file['error']))
                     $error ='Error #'.$file['error']; //TODO: Transplate to string.
                 else
@@ -492,7 +492,7 @@ Class ThreadEntry {
                  XXX: We're doing it here because it will eventually become a thread post comment (hint: comments coming!)
                  XXX: logNote must watch for possible loops
                */
-                $this->getTicket()->logNote('File Upload Error', $error, 'SYSTEM', false);
+                $this->getTicket()->logNote(__('File Upload Error'), $error, 'SYSTEM', false);
             }
 
         }
@@ -524,9 +524,10 @@ Class ThreadEntry {
             $error = $attachment['error'];
 
             if(!$error)
-                $error = 'Unable to import attachment - '.$attachment['name'];
+                $error = sprintf(_S('Unable to import attachment - %s'),$attachment['name']);
 
-            $this->getTicket()->logNote('File Import Error', $error, 'SYSTEM', false);
+            $this->getTicket()->logNote(_S('File Import Error'), $error,
+                _S('SYSTEM'), false);
         }
 
         return $id;
@@ -538,7 +539,11 @@ Class ThreadEntry {
     */
     function saveAttachment(&$file) {
 
-        if(!($fileId=is_numeric($file)?$file:AttachmentFile::save($file)))
+        if (is_numeric($file))
+            $fileId = $file;
+        elseif (is_array($file) && isset($file['id']))
+            $fileId = $file['id'];
+        elseif (!($fileId = AttachmentFile::save($file)))
             return 0;
 
         $inline = is_array($file) && @$file['inline'];
@@ -665,11 +670,10 @@ Class ThreadEntry {
             // This mail was sent by this system. It was received due to
             // some kind of mail delivery loop. It should not be considered
             // a response to an existing thread entry
-            if ($ost) $ost->log(LOG_ERR, 'Email loop detected', sprintf(
-               'It appears as though &lt;%s&gt; is being used as a forwarded or
-                fetched email account and is also being used as a user /
-                system account. Please correct the loop or seek technical
-                assistance.', $mailinfo['email']),
+            if ($ost) $ost->log(LOG_ERR, _S('Email loop detected'), sprintf(
+                _S('It appears as though &lt;%s&gt; is being used as a forwarded or fetched email account and is also being used as a user / system account. Please correct the loop or seek technical assistance.'),
+                $mailinfo['email']),
+
                 // This is quite intentional -- don't continue the loop
                 false,
                 // Force the message, even if logging is disabled
@@ -906,9 +910,9 @@ Class ThreadEntry {
         $match = array();
         if ($subject
                 && $mailinfo['email']
-                && preg_match("/#(?:[\p{L}-]+)?([0-9]{1,10})/u", $subject, $match)
+                && preg_match("/\b#(\S+)/u", $subject, $match)
                 //Lookup by ticket number
-                && ($ticket = Ticket::lookupByNumber((int)$match[1]))
+                && ($ticket = Ticket::lookupByNumber($match[1]))
                 //Lookup the user using the email address
                 && ($user = User::lookup(array('emails__address' => $mailinfo['email'])))) {
             //We have a valid ticket and user
@@ -1106,6 +1110,8 @@ Class ThreadEntry {
         // Inline images (attached to the draft)
         $entry->saveAttachments(Draft::getAttachmentIds($body));
 
+        Signal::send('model.created', $entry);
+
         return $entry;
     }
 
@@ -1132,9 +1138,9 @@ class Message extends ThreadEntry {
     function add($vars, &$errors) {
 
         if(!$vars || !is_array($vars) || !$vars['ticketId'])
-            $errors['err'] = 'Missing or invalid data';
+            $errors['err'] = __('Missing or invalid data');
         elseif(!$vars['message'])
-            $errors['message'] = 'Message required';
+            $errors['message'] = __('Message content is required');
 
         if($errors) return false;
 
@@ -1201,9 +1207,9 @@ class Response extends ThreadEntry {
     function add($vars, &$errors) {
 
         if(!$vars || !is_array($vars) || !$vars['ticketId'])
-            $errors['err'] = 'Missing or invalid data';
+            $errors['err'] = __('Missing or invalid data');
         elseif(!$vars['response'])
-            $errors['response'] = 'Response required';
+            $errors['response'] = __('Response content is required');
 
         if($errors) return false;
 
@@ -1251,9 +1257,9 @@ class Note extends ThreadEntry {
 
         //Check required params.
         if(!$vars || !is_array($vars) || !$vars['ticketId'])
-            $errors['err'] = 'Missing or invalid data';
+            $errors['err'] = __('Missing or invalid data');
         elseif(!$vars['note'])
-            $errors['note'] = 'Note required';
+            $errors['note'] = __('Note content is required');
 
         if($errors) return false;
 
@@ -1289,12 +1295,13 @@ class ThreadBody /* extends SplString */ {
     function __construct($body, $type='text', $options=array()) {
         $type = strtolower($type);
         if (!in_array($type, static::$types))
-            throw new Exception($type.': Unsupported ThreadBody type');
+            throw new Exception("$type: Unsupported ThreadBody type");
         $this->body = (string) $body;
         if (strlen($this->body) > 250000) {
             $max_packet = db_get_variable('max_allowed_packet', 'global');
             // Truncate just short of the max_allowed_packet
-            $this->body = substr($this->body, 0, $max_packet - 2048) . ' ... (truncated)';
+            $this->body = substr($this->body, 0, $max_packet - 2048) . ' ... '
+               . _S('(truncated)');
         }
         $this->type = $type;
         $this->options = array_merge($this->options, $options);
@@ -1368,12 +1375,11 @@ class ThreadBody /* extends SplString */ {
     }
 
     function display($format=false) {
-        throw new Exception('display: Abstract dispplay() method not implemented');
+        throw new Exception('display: Abstract display() method not implemented');
     }
 
     function getSearchable() {
-        return $this->body;
-        // TODO: Normalize Unicode string
+        return Format::searchable($this->body);
     }
 
     static function fromFormattedText($text, $format=false) {
@@ -1445,9 +1451,9 @@ class HtmlThreadBody extends ThreadBody {
 
     function getSearchable() {
         // <br> -> \n
-        $body = preg_replace('/\<br(\s*)?\/?\>/i', "\n", $this->body);
-        return Format::striptags($body);
-        // TODO: Normalize Unicode string
+        $body = preg_replace(array('`<br(\s*)?/?>`i', '`</div>`i'), "\n", $this->body);
+        $body = Format::htmldecode(Format::striptags($body));
+        return Format::searchable($body);
     }
 
     function display($output=false) {
diff --git a/include/class.ticket.php b/include/class.ticket.php
index 6ba653bf4e971355df3f06a9e0a044cf5537e998..c4b6d3e94a36cb9c38b83ad0a411f4779151c9d9 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -43,6 +43,7 @@ class Ticket {
 
     var $lastMsgId;
 
+    var $status;
     var $dept;  //Dept obj
     var $sla;   // SLA obj
     var $staff; //Staff obj
@@ -53,6 +54,12 @@ class Ticket {
 
     var $thread; //Thread obj.
 
+    // Status -- listed here until we have a formal status class
+    static $STATUSES = array(
+        /* @trans */ 'open',
+        /* @trans */ 'closed',
+    );
+
     function Ticket($id) {
         $this->id = 0;
         $this->load($id);
@@ -91,6 +98,7 @@ class Ticket {
         $this->loadDynamicData();
 
         //Reset the sub classes (initiated ondemand)...good for reloads.
+        $this->status= null;
         $this->staff = null;
         $this->client = null;
         $this->team  = null;
@@ -107,27 +115,47 @@ class Ticket {
 
     function loadDynamicData() {
         if (!$this->_answers) {
-            foreach (DynamicFormEntry::forTicket($this->getId(), true) as $form)
-                foreach ($form->getAnswers() as $answer)
-                    if ($tag = mb_strtolower($answer->getField()->get('name')))
+            foreach (DynamicFormEntry::forTicket($this->getId(), true) as $form) {
+                foreach ($form->getAnswers() as $answer) {
+                    $tag = mb_strtolower($answer->getField()->get('name'))
+                        ?: 'field.' . $answer->getField()->get('id');
                         $this->_answers[$tag] = $answer;
+                }
+            }
         }
+        return $this->_answers;
     }
 
     function reload() {
         return $this->load();
     }
 
+    function hasState($state) {
+        return  (strcasecmp($this->getState(), $state)==0);
+    }
+
     function isOpen() {
-        return (strcasecmp($this->getStatus(),'Open')==0);
+        return $this->hasState('open');
     }
 
     function isReopened() {
         return ($this->getReopenDate());
     }
 
+    function isReopenable() {
+        return $this->getStatus()->isReopenable();
+    }
+
     function isClosed() {
-        return (strcasecmp($this->getStatus(),'Closed')==0);
+         return $this->hasState('closed');
+    }
+
+    function isArchived() {
+         return $this->hasState('archived');
+    }
+
+    function isDeleted() {
+         return $this->hasState('deleted');
     }
 
     function isAssigned() {
@@ -291,8 +319,24 @@ class Ticket {
         return $this->ht['closed'];
     }
 
+    function getStatusId() {
+        return $this->ht['status_id'];
+    }
+
     function getStatus() {
-        return $this->ht['status'];
+
+        if (!$this->status && $this->getStatusId())
+            $this->status = TicketStatus::lookup($this->getStatusId());
+
+        return $this->status;
+    }
+
+    function getState() {
+
+        if (!$this->getStatus())
+            return '';
+
+        return $this->getStatus()->getState();
     }
 
     function getDeptId() {
@@ -666,7 +710,7 @@ class Ticket {
                      $collabs[] = $c;
             }
 
-            $this->logNote('Collaborators Removed',
+            $this->logNote(_S('Collaborators Removed'),
                     implode("<br>", $collabs), $thisstaff, false);
         }
 
@@ -784,21 +828,80 @@ class Ticket {
     }
 
     //Status helper.
-    function setStatus($status) {
+    function setStatus($status, $comments='') {
+        global $thisstaff;
 
-        if(strcasecmp($this->getStatus(), $status)==0)
-            return true; //No changes needed.
+        if ($status && is_numeric($status))
+            $status = TicketStatus::lookup($status);
 
-        switch(strtolower($status)) {
-            case 'open':
-                return $this->reopen();
-                break;
+        if (!$status || !$status instanceof TicketStatus)
+            return false;
+
+        // XXX: intercept deleted status and do hard delete
+        if (!strcasecmp($status->getState(), 'deleted'))
+            return $this->delete($comments);
+
+        if ($this->getStatusId() == $status->getId())
+            return true;
+
+        $sql = 'UPDATE '.TICKET_TABLE.' SET updated=NOW() '.
+               ' ,status_id='.db_input($status->getId());
+
+        $ecb = null;
+        switch($status->getState()) {
             case 'closed':
-                return $this->close();
+                $sql.=', closed=NOW(), duedate=NULL ';
+                if ($thisstaff)
+                    $sql.=', staff_id='.db_input($thisstaff->getId());
+
+                $ecb = function($t) {
+                    $t->reload();
+                    $t->logEvent('closed');
+                    $t->deleteDrafts();
+                };
                 break;
+            case 'open':
+                // TODO: check current status if it allows for reopening
+                if ($this->isClosed()) {
+                    $sql .= ',closed=NULL, reopened=NOW() ';
+                    $ecb = function ($t) {
+                        $t->logEvent('reopened', 'closed');
+                    };
+                }
+
+                // If the ticket is not open then clear answered flag
+                if (!$this->isOpen())
+                    $sql .= ', isanswered = 0 ';
+                break;
+            default:
+                return false;
+
         }
 
-        return false;
+        $sql.=' WHERE ticket_id='.db_input($this->getId());
+
+        if (!db_query($sql) || !db_affected_rows())
+            return false;
+
+        // Log status change b4 reload
+        $note = sprintf(__('Status changed from %s to %s by %s'),
+                $this->getStatus(),
+                $status,
+                $thisstaff ?: 'SYSTEM');
+
+        $alert = false;
+        if ($comments) {
+            $note .= sprintf('<hr>%s', $comments);
+            // Send out alerts if comments are included
+            $alert = true;
+        }
+
+        $this->logNote(__('Status Changed'), $note, $thisstaff, $alert);
+
+        // Log events via callback
+        if ($ecb) $ecb($this);
+
+        return true;
     }
 
     function setState($state, $alerts=false) {
@@ -840,42 +943,19 @@ class Ticket {
         return (db_query($sql) && db_affected_rows());
     }
 
-    //Close the ticket
-    function close() {
-        global $thisstaff;
-
-        $sql='UPDATE '.TICKET_TABLE.' SET closed=NOW(),isoverdue=0, duedate=NULL, updated=NOW(), status='.db_input('closed');
-        if($thisstaff) //Give the closing  staff credit.
-            $sql.=', staff_id='.db_input($thisstaff->getId());
-
-        $sql.=' WHERE ticket_id='.db_input($this->getId());
-
-        if(!db_query($sql) || !db_affected_rows())
-            return false;
-
-        $this->reload();
-        $this->logEvent('closed');
-        $this->deleteDrafts();
-
-        return true;
-    }
-
-    //set status to open on a closed ticket.
-    function reopen($isanswered=0) {
-
-        $sql='UPDATE '.TICKET_TABLE.' SET closed=NULL, updated=NOW(), reopened=NOW() '
-            .' ,status='.db_input('open')
-            .' ,isanswered='.db_input($isanswered)
-            .' WHERE ticket_id='.db_input($this->getId());
+    function reopen() {
+        global $cfg;
 
-        if (!db_query($sql) || !db_affected_rows())
+        if (!$this->isClosed())
             return false;
 
-        $this->logEvent('reopened', 'closed');
-        $this->ht['status'] = 'open';
-        $this->ht['isanswerd'] = $isanswered;
+        // Set status to open based on current closed status settings
+        // If the closed status doesn't have configured "reopen" status then use the
+        // the default ticket status.
+        if (!($status=$this->getStatus()->getReopenStatus()))
+            $status = $cfg->getDefaultTicketStatusId();
 
-        return true;
+        return $status ? $this->setStatus($status, 'Reopened') : false;
     }
 
     function onNewTicket($message, $autorespond=true, $alertstaff=true) {
@@ -970,8 +1050,10 @@ class Ticket {
         global $ost, $cfg;
 
         //Log the limit notice as a warning for admin.
-        $msg=sprintf('Max open tickets (%d) reached  for %s ', $cfg->getMaxOpenTickets(), $this->getEmail());
-        $ost->logWarning('Max. Open Tickets Limit ('.$this->getEmail().')', $msg);
+        $msg=sprintf(_S('Maximum open tickets (%1$d) reached for %2$s'),
+            $cfg->getMaxOpenTickets(), $this->getEmail());
+        $ost->logWarning(sprintf(_S('Maximum Open Tickets Limit (%s)'),$this->getEmail()),
+            $msg);
 
         if(!$sendNotice || !$cfg->sendOverLimitNotice())
             return true;
@@ -991,11 +1073,12 @@ class Ticket {
         $user = $this->getOwner();
 
         //Alert admin...this might be spammy (no option to disable)...but it is helpful..I think.
-        $alert='Max. open tickets reached for '.$this->getEmail()."\n"
-              .'Open ticket: '.$user->getNumOpenTickets()."\n"
-              .'Max Allowed: '.$cfg->getMaxOpenTickets()."\n\nNotice sent to the user.";
+        $alert=sprintf(__('Maximum open tickets reached for %s.'), $this->getEmail())."\n"
+              .sprintf(__('Open tickets: %d'), $user->getNumOpenTickets())."\n"
+              .sprintf(__('Max allowed: %d'), $cfg->getMaxOpenTickets())
+              ."\n\n".__("Notice sent to the user.");
 
-        $ost->alertAdmin('Overlimit Notice', $alert);
+        $ost->alertAdmin(__('Overlimit Notice'), $alert);
 
         return true;
     }
@@ -1035,7 +1118,7 @@ class Ticket {
 
         $vars = array_merge($vars, array(
                     'message' => (string) $entry,
-                    'poster' => $poster? $poster : 'A collaborator',
+                    'poster' => $poster ?: _S('A collaborator'),
                     )
                 );
 
@@ -1078,8 +1161,9 @@ class Ticket {
             }
         }
 
-        // Reopen  if closed.
-        if($this->isClosed()) $this->reopen();
+        // Reopen if closed AND reopenable
+        if ($this->isClosed() && $this->isReopenable())
+            $this->reopen();
 
        /**********   double check auto-response  ************/
         if (!($user = $message->getUser()))
@@ -1127,11 +1211,12 @@ class Ticket {
 
         $this->reload();
 
-        $comments = $comments?$comments:'Ticket assignment';
-        $assigner = $thisstaff?$thisstaff:'SYSTEM (Auto Assignment)';
+        $comments = $comments ?: _S('Ticket assignment');
+        $assigner = $thisstaff ?: _S('SYSTEM (Auto Assignment)');
 
         //Log an internal note - no alerts on the internal note.
-        $note = $this->logNote('Ticket Assigned to '.$assignee->getName(),
+        $note = $this->logNote(
+            sprintf(_S('Ticket Assigned to %s'), $assignee->getName()),
             $comments, $assigner, false);
 
         //See if we need to send alerts
@@ -1383,7 +1468,8 @@ class Ticket {
             $this->selectSLAId();
 
         /*** log the transfer comments as internal note - with alerts disabled - ***/
-        $title='Ticket transfered from '.$currentDept.' to '.$this->getDeptName();
+        $title=sprintf(_S('Ticket transferred from %1$s to %2$s'),
+            $currentDept, $this->getDeptName());
         $comments=$comments?$comments:$title;
         $note = $this->logNote($title, $comments, $thisstaff, false);
 
@@ -1532,14 +1618,14 @@ class Ticket {
         $this->recipients = null;
 
         //Log an internal note
-        $note = sprintf('%s changed ticket ownership to %s',
+        $note = sprintf(_S('%s changed ticket ownership to %s'),
                 $thisstaff->getName(), $user->getName());
 
         //Remove the new owner from list of collaborators
         $c = Collaborator::lookup(array('userId' => $user->getId(),
                     'ticketId' => $this->getId()));
         if ($c && $c->remove())
-            $note.= ' (removed as collaborator)';
+            $note.= ' '._S('(removed as collaborator)');
 
         $this->logNote('Ticket ownership changed', $note);
 
@@ -1580,15 +1666,17 @@ class Ticket {
                 if (($user=User::fromVars($recipient)))
                     if ($c=$this->addCollaborator($user, $info, $errors))
                         $collabs[] = sprintf('%s%s',
-                                (string) $c,
-                                $recipient['source'] ? " via {$recipient['source']}" : ''
-                                );
+                            (string) $c,
+                            $recipient['source']
+                                ? " ".sprintf(_S('via %s'), $recipient['source'])
+                                : ''
+                            );
             }
             //TODO: Can collaborators add others?
             if ($collabs) {
                 //TODO: Change EndUser to name of  user.
-                $this->logNote('Collaborators added by enduser',
-                        implode("<br>", $collabs), 'EndUser', false);
+                $this->logNote(_S('Collaborators added by end user'),
+                        implode("<br>", $collabs), _S('End User'), false);
             }
         }
 
@@ -1680,7 +1768,7 @@ class Ticket {
                     $this->replaceVars($canned->getPlainText()));
 
         $info = array('msgId' => $msgId,
-                      'poster' => 'SYSTEM (Canned Reply)',
+                      'poster' => __('SYSTEM (Canned Reply)'),
                       'response' => $response,
                       'cannedattachments' => $files);
 
@@ -1736,9 +1824,10 @@ class Ticket {
         if(!($response = $this->getThread()->addResponse($vars, $errors)))
             return null;
 
-        //Set status - if checked.
-        if(isset($vars['reply_ticket_status']) && $vars['reply_ticket_status'])
-            $this->setStatus($vars['reply_ticket_status']);
+        // Set status - if checked.
+        if ($vars['reply_status_id']
+                && $vars['reply_status_id'] != $this->getStatusId())
+            $this->setStatus($vars['reply_status_id']);
 
         if($thisstaff && $this->isOpen() && !$this->getStaffId()
                 && $cfg->autoClaimTickets())
@@ -1746,7 +1835,7 @@ class Ticket {
 
         $this->onResponse(); //do house cleaning..
 
-        /* email the user??  - if disabled - the bail out */
+        /* email the user??  - if disabled - then bail out */
         if(!$alert) return $response;
 
         $dept = $this->getDept();
@@ -1865,9 +1954,9 @@ class Ticket {
         // Get assigned staff just in case the ticket is closed.
         $assignee = $this->getStaff();
 
-        //Set state: Error on state change not critical!
-        if(isset($vars['state']) && $vars['state']) {
-            if($this->setState($vars['state']))
+        if ($vars['note_status_id']
+                && ($status=TicketStatus::lookup($vars['note_status_id']))) {
+            if ($this->setStatus($status))
                 $this->reload();
         }
 
@@ -1951,7 +2040,8 @@ class Ticket {
         exit;
     }
 
-    function delete() {
+    function delete($comments='') {
+        global $ost, $thisstaff;
 
         //delete just orphaned ticket thread & associated attachments.
         // Fetch thread prior to removing ticket entry
@@ -1968,6 +2058,18 @@ class Ticket {
 
         $this->deleteDrafts();
 
+        // Log delete
+        $log = sprintf(__('Ticket #%1$s deleted by %2$s'),
+                $this->getNumber(),
+                $thisstaff ? $thisstaff->getName() : __('SYSTEM'));
+
+        if ($comments)
+            $log .= sprintf('<hr>%s', $comments);
+
+        $ost->logDebug(
+                sprintf( __('Ticket #%s deleted'), $this->getNumber()),
+                $log);
+
         return true;
     }
 
@@ -1983,25 +2085,36 @@ class Ticket {
             return false;
 
         $fields=array();
-        $fields['topicId']  = array('type'=>'int',      'required'=>1, 'error'=>'Help topic required');
-        $fields['slaId']    = array('type'=>'int',      'required'=>0, 'error'=>'Select SLA');
-        $fields['duedate']  = array('type'=>'date',     'required'=>0, 'error'=>'Invalid date - must be MM/DD/YY');
+        $fields['topicId']  = array('type'=>'int',      'required'=>1, 'error'=>__('Help topic selection is required'));
+        $fields['slaId']    = array('type'=>'int',      'required'=>0, 'error'=>__('Select a valid SLA'));
+        $fields['duedate']  = array('type'=>'date',     'required'=>0, 'error'=>__('Invalid date format - must be MM/DD/YY'));
 
-        $fields['note']     = array('type'=>'text',     'required'=>1, 'error'=>'Reason for the update required');
-        $fields['user_id']  = array('type'=>'int',      'required'=>0, 'error'=>'Invalid user-id');
+        $fields['note']     = array('type'=>'text',     'required'=>1, 'error'=>__('A reason for the update is required'));
+        $fields['user_id']  = array('type'=>'int',      'required'=>0, 'error'=>__('Invalid user-id'));
 
         if(!Validator::process($fields, $vars, $errors) && !$errors['err'])
-            $errors['err'] = 'Missing or invalid data - check the errors and try again';
+            $errors['err'] = __('Missing or invalid data - check the errors and try again');
 
         if($vars['duedate']) {
             if($this->isClosed())
-                $errors['duedate']='Due date can NOT be set on a closed ticket';
+                $errors['duedate']=__('Due date can NOT be set on a closed ticket');
             elseif(!$vars['time'] || strpos($vars['time'],':')===false)
-                $errors['time']='Select time';
+                $errors['time']=__('Select a time from the list');
             elseif(strtotime($vars['duedate'].' '.$vars['time'])===false)
-                $errors['duedate']='Invalid due date';
+                $errors['duedate']=__('Invalid due date');
             elseif(strtotime($vars['duedate'].' '.$vars['time'])<=time())
-                $errors['duedate']='Due date must be in the future';
+                $errors['duedate']=__('Due date must be in the future');
+        }
+
+        // Validate dynamic meta-data
+        $forms = DynamicFormEntry::forTicket($this->getId());
+        foreach ($forms as $form) {
+            // Don't validate deleted forms
+            if (!in_array($form->getId(), $vars['forms']))
+                continue;
+            $form->setSource($_POST);
+            if (!$form->isValid())
+                $errors = array_merge($errors, $form->errors());
         }
 
         if($errors) return false;
@@ -2024,13 +2137,26 @@ class Ticket {
             return false;
 
         if(!$vars['note'])
-            $vars['note']=sprintf('Ticket Updated by %s', $thisstaff->getName());
+            $vars['note']=sprintf(_S('Ticket details updated by %s'), $thisstaff->getName());
 
-        $this->logNote('Ticket Updated', $vars['note'], $thisstaff);
+        $this->logNote(_S('Ticket Updated'), $vars['note'], $thisstaff);
 
         // Decide if we need to keep the just selected SLA
         $keepSLA = ($this->getSLAId() != $vars['slaId']);
 
+        // Update dynamic meta-data
+        foreach ($forms as $f) {
+            // Drop deleted forms
+            $idx = array_search($f->getId(), $vars['forms']);
+            if ($idx === false) {
+                $f->delete();
+            }
+            else {
+                $f->set('sort', $idx);
+                $f->save();
+            }
+        }
+
         // Reload the ticket so we can do further checking
         $this->reload();
 
@@ -2047,6 +2173,7 @@ class Ticket {
             $this->clearOverdue();
         }
 
+        Signal::send('model.updated', $this);
         return true;
     }
 
@@ -2085,16 +2212,9 @@ class Ticket {
         return self::lookup(self:: getIdByNumber($number, $email));
     }
 
-    function genRandTicketNumber($len = EXT_TICKET_ID_LEN) {
-
-        //We can allow collissions...number and email must be unique ...so
-        // same number with diff emails is ok.. But for clarity...we are going to make sure it is unique.
-        $number = Misc::randNumber($len);
-        if(db_num_rows(db_query('SELECT ticket_id FROM '.TICKET_TABLE.'
-                        WHERE `number`='.db_input($number))))
-            return Ticket::genRandTicketNumber($len);
-
-        return $number;
+    static function isTicketNumberUnique($number) {
+        return 0 == db_num_rows(db_query(
+            'SELECT ticket_id FROM '.TICKET_TABLE.' WHERE `number`='.db_input($number)));
     }
 
     function getIdByMessageId($mid, $email) {
@@ -2121,12 +2241,13 @@ class Ticket {
         if(!$staff || (!is_object($staff) && !($staff=Staff::lookup($staff))) || !$staff->isStaff())
             return null;
 
-        $where = array('(ticket.staff_id='.db_input($staff->getId()) .' AND ticket.status="open")');
+        $where = array('(ticket.staff_id='.db_input($staff->getId()) .' AND
+                    status.state="open")');
         $where2 = '';
 
         if(($teams=$staff->getTeams()))
             $where[] = ' ( ticket.team_id IN('.implode(',', db_input(array_filter($teams)))
-                        .') AND ticket.status="open")';
+                        .') AND status.state="open")';
 
         if(!$staff->showAssignedOnly() && ($depts=$staff->getDepts())) //Staff with limited access just see Assigned tickets.
             $where[] = 'ticket.dept_id IN('.implode(',', db_input($depts)).') ';
@@ -2138,31 +2259,42 @@ class Ticket {
 
         $sql =  'SELECT \'open\', count( ticket.ticket_id ) AS tickets '
                 .'FROM ' . TICKET_TABLE . ' ticket '
-                .'WHERE ticket.status = \'open\' '
-                .'AND ticket.isanswered =0 '
+                .'INNER JOIN '.TICKET_STATUS_TABLE. ' status
+                    ON (ticket.status_id=status.id
+                            AND status.state=\'open\') '
+                .'WHERE ticket.isanswered = 0 '
                 . $where . $where2
 
                 .'UNION SELECT \'answered\', count( ticket.ticket_id ) AS tickets '
                 .'FROM ' . TICKET_TABLE . ' ticket '
-                .'WHERE ticket.status = \'open\' '
-                .'AND ticket.isanswered =1 '
+                .'INNER JOIN '.TICKET_STATUS_TABLE. ' status
+                    ON (ticket.status_id=status.id
+                            AND status.state=\'open\') '
+                .'WHERE ticket.isanswered = 1 '
                 . $where
 
                 .'UNION SELECT \'overdue\', count( ticket.ticket_id ) AS tickets '
                 .'FROM ' . TICKET_TABLE . ' ticket '
-                .'WHERE ticket.status = \'open\' '
-                .'AND ticket.isoverdue =1 '
+                .'INNER JOIN '.TICKET_STATUS_TABLE. ' status
+                    ON (ticket.status_id=status.id
+                            AND status.state=\'open\') '
+                .'WHERE ticket.isoverdue =1 '
                 . $where
 
                 .'UNION SELECT \'assigned\', count( ticket.ticket_id ) AS tickets '
                 .'FROM ' . TICKET_TABLE . ' ticket '
-                .'WHERE ticket.status = \'open\' '
-                .'AND ticket.staff_id = ' . db_input($staff->getId()) . ' '
+                .'INNER JOIN '.TICKET_STATUS_TABLE. ' status
+                    ON (ticket.status_id=status.id
+                            AND status.state=\'open\') '
+                .'WHERE ticket.staff_id = ' . db_input($staff->getId()) . ' '
                 . $where
 
                 .'UNION SELECT \'closed\', count( ticket.ticket_id ) AS tickets '
                 .'FROM ' . TICKET_TABLE . ' ticket '
-                .'WHERE ticket.status = \'closed\' '
+                .'INNER JOIN '.TICKET_STATUS_TABLE. ' status
+                    ON (ticket.status_id=status.id
+                            AND status.state=\'closed\' ) '
+                .'WHERE 1 '
                 . $where;
 
         $res = db_query($sql);
@@ -2226,13 +2358,16 @@ class Ticket {
             global $ost;
             $errors = array(
                 'errno' => 403,
-                'err' => 'This help desk is for use by authorized users only');
-            $ost->logWarning('Ticket Denied', $message, false);
+                'err' => __('This help desk is for use by authorized users only'));
+            $ost->logWarning(_S('Ticket Denied'), $message, false);
             return 0;
         };
 
+        Signal::send('ticket.create.before', null, $vars);
+
         // Create and verify the dynamic form entry for the new ticket
         $form = TicketForm::getNewInstance();
+        $form->setSource($vars);
         // If submitting via email, ensure we have a subject and such
         foreach ($form->getFields() as $field) {
             $fname = $field->get('name');
@@ -2279,7 +2414,7 @@ class Ticket {
 
             //Make sure the email address is not banned
             if (TicketFilter::isBanned($vars['email'])) {
-                return $reject_ticket('Banned email - '.$vars['email']);
+                return $reject_ticket(sprintf(_S('Banned email - %s'), $vars['email']));
             }
 
             //Make sure the open ticket limit hasn't been reached. (LOOP CONTROL)
@@ -2289,9 +2424,9 @@ class Ticket {
                     && ($openTickets=$_user->getNumOpenTickets())
                     && ($openTickets>=$cfg->getMaxOpenTickets()) ) {
 
-                $errors = array('err' => "You've reached the maximum open tickets allowed.");
-                $ost->logWarning('Ticket denied -'.$vars['email'],
-                        sprintf('Max open tickets (%d) reached for %s ',
+                $errors = array('err' => __("You've reached the maximum open tickets allowed."));
+                $ost->logWarning(sprintf(_S('Ticket denied - %s'), $vars['email']),
+                        sprintf(_S('Max open tickets (%1$d) reached for %2$s'),
                             $cfg->getMaxOpenTickets(), $vars['email']),
                         false);
 
@@ -2315,43 +2450,43 @@ class Ticket {
         if ($ticket_filter
                 && ($filter=$ticket_filter->shouldReject())) {
             return $reject_ticket(
-                sprintf('Ticket rejected ( %s) by filter "%s"',
+                sprintf(_S('Ticket rejected (%s) by filter "%s"'),
                     $vars['email'], $filter->getName()));
         }
 
         $id=0;
         $fields=array();
-        $fields['message']  = array('type'=>'*',     'required'=>1, 'error'=>'Message required');
+        $fields['message']  = array('type'=>'*',     'required'=>1, 'error'=>__('Message content is required'));
         switch (strtolower($origin)) {
             case 'web':
-                $fields['topicId']  = array('type'=>'int',  'required'=>1, 'error'=>'Select help topic');
+                $fields['topicId']  = array('type'=>'int',  'required'=>1, 'error'=>__('Select a help topic'));
                 break;
             case 'staff':
-                $fields['deptId']   = array('type'=>'int',  'required'=>0, 'error'=>'Dept. required');
-                $fields['topicId']  = array('type'=>'int',  'required'=>1, 'error'=>'Topic required');
-                $fields['duedate']  = array('type'=>'date', 'required'=>0, 'error'=>'Invalid date - must be MM/DD/YY');
+                $fields['deptId']   = array('type'=>'int',  'required'=>0, 'error'=>__('Department selection is required'));
+                $fields['topicId']  = array('type'=>'int',  'required'=>1, 'error'=>__('Help topic selection is required'));
+                $fields['duedate']  = array('type'=>'date', 'required'=>0, 'error'=>__('Invalid date format - must be MM/DD/YY'));
             case 'api':
-                $fields['source']   = array('type'=>'string', 'required'=>1, 'error'=>'Indicate source');
+                $fields['source']   = array('type'=>'string', 'required'=>1, 'error'=>__('Indicate ticket source'));
                 break;
             case 'email':
-                $fields['emailId']  = array('type'=>'int',  'required'=>1, 'error'=>'Email unknown');
+                $fields['emailId']  = array('type'=>'int',  'required'=>1, 'error'=>__('Unknown system email'));
                 break;
             default:
                 # TODO: Return error message
-                $errors['err']=$errors['origin'] = 'Invalid origin given';
+                $errors['err']=$errors['origin'] = __('Invalid ticket origin given');
         }
 
         if(!Validator::process($fields, $vars, $errors) && !$errors['err'])
-            $errors['err'] ='Missing or invalid data - check the errors and try again';
+            $errors['err'] =__('Missing or invalid data - check the errors and try again');
 
         //Make sure the due date is valid
         if($vars['duedate']) {
             if(!$vars['time'] || strpos($vars['time'],':')===false)
-                $errors['time']='Select time';
+                $errors['time']=__('Select a time from the list');
             elseif(strtotime($vars['duedate'].' '.$vars['time'])===false)
-                $errors['duedate']='Invalid due date';
+                $errors['duedate']=__('Invalid due date');
             elseif(strtotime($vars['duedate'].' '.$vars['time'])<=time())
-                $errors['duedate']='Due date must be in the future';
+                $errors['duedate']=__('Due date must be in the future');
         }
 
         if (!$errors) {
@@ -2374,7 +2509,7 @@ class Ticket {
                     // are still acceptable
                     if (!Organization::forDomain($domain)) {
                         return $reject_ticket(
-                            sprintf('Ticket rejected (%s) (unregistered client)',
+                            sprintf(_S('Ticket rejected (%s) (unregistered client)'),
                                 $vars['email']));
                     }
                 }
@@ -2382,7 +2517,7 @@ class Ticket {
                 $user_form = UserForm::getUserForm()->getForm($vars);
                 if (!$user_form->isValid($field_filter('user'))
                         || !($user=User::fromVars($user_form->getClean())))
-                    $errors['user'] = 'Incomplete client information';
+                    $errors['user'] = __('Incomplete client information');
             }
         }
 
@@ -2405,6 +2540,8 @@ class Ticket {
         if ($errors)
             return 0;
 
+        Signal::send('ticket.create.validated', null, $vars);
+
         # Some things will need to be unpacked back into the scope of this
         # function
         if (isset($vars['autorespond']))
@@ -2422,6 +2559,7 @@ class Ticket {
         // members can always add more forms now
 
         // OK...just do it.
+        $statusId = $vars['statusId'];
         $deptId = $vars['deptId']; //pre-selected Dept if any.
         $source = ucfirst($vars['source']);
 
@@ -2452,6 +2590,7 @@ class Ticket {
         // Intenal mapping magic...see if we need to override anything
         if (isset($topic)) {
             $deptId = $deptId ?: $topic->getDeptId();
+            $statusId = $statusId ?: $topic->getStatusId();
             $priority = $form->getAnswer('priority');
             if (!$priority || !$priority->getIdValue())
                 $form->setAnswer('priority', null, $topic->getPriorityId());
@@ -2486,18 +2625,20 @@ class Ticket {
         if (!$priority || !$priority->getIdValue())
             $form->setAnswer('priority', null, $cfg->getDefaultPriorityId());
         $deptId = $deptId ?: $cfg->getDefaultDeptId();
+        $statusId = $statusId ?: $cfg->getDefaultTicketStatusId();
         $topicId = isset($topic) ? $topic->getId() : 0;
         $ipaddress = $vars['ip'] ?: $_SERVER['REMOTE_ADDR'];
         $source = $source ?: 'Web';
 
         //We are ready son...hold on to the rails.
-        $number = Ticket::genRandTicketNumber();
+        $number = $topic ? $topic->getNewTicketNumber() : $cfg->getNewTicketNumber();
         $sql='INSERT INTO '.TICKET_TABLE.' SET created=NOW() '
             .' ,lastmessage= NOW()'
             .' ,user_id='.db_input($user->getId())
             .' ,`number`='.db_input($number)
             .' ,dept_id='.db_input($deptId)
             .' ,topic_id='.db_input($topicId)
+            .' ,status_id='.db_input($statusId)
             .' ,ip_address='.db_input($ipaddress)
             .' ,source='.db_input($source);
 
@@ -2514,13 +2655,6 @@ class Ticket {
 
         /* -------------------- POST CREATE ------------------------ */
 
-        if(!$cfg->useRandomIds()) {
-            //Sequential ticket number support really..really suck arse.
-            //To make things really easy we are going to use autoincrement ticket_id.
-            db_query('UPDATE '.TICKET_TABLE.' SET `number`='.db_input($id).' WHERE ticket_id='.$id.' LIMIT 1');
-            //TODO: RETHING what happens if this fails?? [At the moment on failure random ID is used...making stuff usable]
-        }
-
         // Save the (common) dynamic form
         $form->setTicketId($id);
         $form->save();
@@ -2551,14 +2685,13 @@ class Ticket {
             //TODO: Can collaborators add others?
             if ($collabs) {
                 //TODO: Change EndUser to name of  user.
-                $ticket->logNote(sprintf('Collaborators for %s organization added',
+                $ticket->logNote(sprintf(_S('Collaborators for %s organization added'),
                         $org->getName()),
                     implode("<br>", $collabs), $org->getName(), false);
             }
         }
 
         //post the message.
-        unset($vars['cannedattachments']); //Ticket::open() might have it set as part of  open & respond.
         $vars['title'] = $vars['subject']; //Use the initial subject as title of the post.
         $vars['userId'] = $ticket->getUserId();
         $message = $ticket->postMessage($vars , $origin, false);
@@ -2574,9 +2707,9 @@ class Ticket {
             // Auto assign staff or team - auto assignment based on filter
             // rules. Both team and staff can be assigned
             if ($vars['staffId'])
-                 $ticket->assignToStaff($vars['staffId'], 'Auto Assignment');
+                 $ticket->assignToStaff($vars['staffId'], _S('Auto Assignment'));
             if ($vars['teamId'])
-                $ticket->assignToTeam($vars['teamId'], 'Auto Assignment');
+                $ticket->assignToTeam($vars['teamId'], _S('Auto Assignment'));
         }
 
         /**********   double check auto-response  ************/
@@ -2621,6 +2754,9 @@ class Ticket {
         /* Start tracking ticket lifecycle events */
         $ticket->logEvent('created');
 
+        // Fire post-create signal (for extra email sending, searching)
+        Signal::send('model.created', $ticket);
+
         /* Phew! ... time for tea (KETEPA) */
 
         return $ticket;
@@ -2633,21 +2769,26 @@ class Ticket {
         if(!$thisstaff || !$thisstaff->canCreateTickets()) return false;
 
         if($vars['source'] && !in_array(strtolower($vars['source']),array('email','phone','other')))
-            $errors['source']='Invalid source - '.Format::htmlchars($vars['source']);
+            $errors['source']=sprintf(__('Invalid source given - %s'),Format::htmlchars($vars['source']));
 
         if (!$vars['uid']) {
             //Special validation required here
             if (!$vars['email'] || !Validator::is_email($vars['email']))
-                $errors['email'] = 'Valid email required';
+                $errors['email'] = __('Valid email address is required');
 
             if (!$vars['name'])
-                $errors['name'] = 'Name required';
+                $errors['name'] = __('Name is required');
         }
 
         if (!$thisstaff->canAssignTickets())
             unset($vars['assignId']);
 
-        if(!($ticket=Ticket::create($vars, $errors, 'staff', false)))
+        $create_vars = $vars;
+        $tform = TicketForm::objects()->one()->getForm($create_vars);
+        $create_vars['cannedattachments']
+            = $tform->getField('message')->getWidget()->getAttachments()->getClean();
+
+        if(!($ticket=Ticket::create($create_vars, $errors, 'staff', false)))
             return false;
 
         $vars['msgId']=$ticket->getLastMsgId();
@@ -2656,11 +2797,9 @@ class Ticket {
         $response = null;
         if($vars['response'] && $thisstaff->canPostReply()) {
 
-            // unpack any uploaded files into vars.
-            if ($_FILES['attachments'])
-                $vars['files'] = AttachmentFile::format($_FILES['attachments']);
-
             $vars['response'] = $ticket->replaceVars($vars['response']);
+            // $vars['cannedatachments'] contains the attachments placed on
+            // the response form.
             if(($response=$ticket->postReply($vars, $errors, false))) {
                 //Only state supported is closed on response
                 if(isset($vars['ticket_state']) && $thisstaff->canCloseTickets())
@@ -2670,12 +2809,12 @@ class Ticket {
 
         // Not assigned...save optional note if any
         if (!$vars['assignId'] && $vars['note']) {
-            $ticket->logNote('New Ticket', $vars['note'], $thisstaff, false);
+            $ticket->logNote(_S('New Ticket'), $vars['note'], $thisstaff, false);
         }
         else {
             // Not assignment and no internal note - log activity
-            $ticket->logActivity('New Ticket by Staff',
-                'Ticket created by staff -'.$thisstaff->getName());
+            $ticket->logActivity(_S('New Ticket by Agent'),
+                sprintf(_S('Ticket created by agent - %s'), $thisstaff->getName()));
         }
 
         $ticket->reload();
@@ -2733,17 +2872,20 @@ class Ticket {
     function checkOverdue() {
 
         $sql='SELECT ticket_id FROM '.TICKET_TABLE.' T1 '
+            .' INNER JOIN '.TICKET_STATUS_TABLE.' status
+                ON (status.id=T1.status_id AND status.state="open") '
             .' LEFT JOIN '.SLA_TABLE.' T2 ON (T1.sla_id=T2.id AND T2.isactive=1) '
-            .' WHERE status=\'open\' AND isoverdue=0 '
+            .' WHERE isoverdue=0 '
             .' AND ((reopened is NULL AND duedate is NULL AND TIME_TO_SEC(TIMEDIFF(NOW(),T1.created))>=T2.grace_period*3600) '
             .' OR (reopened is NOT NULL AND duedate is NULL AND TIME_TO_SEC(TIMEDIFF(NOW(),reopened))>=T2.grace_period*3600) '
             .' OR (duedate is NOT NULL AND duedate<NOW()) '
             .' ) ORDER BY T1.created LIMIT 50'; //Age upto 50 tickets at a time?
-        //echo $sql;
+
         if(($res=db_query($sql)) && db_num_rows($res)) {
             while(list($id)=db_fetch_row($res)) {
                 if(($ticket=Ticket::lookup($id)) && $ticket->markOverdue())
-                    $ticket->logActivity('Ticket Marked Overdue', 'Ticket flagged as overdue by the system.');
+                    $ticket->logActivity(_S('Ticket Marked Overdue'),
+                        _S('Ticket flagged as overdue by the system.'));
             }
         } else {
             //TODO: Trigger escalation on already overdue tickets - make sure last overdue event > grace_period.
diff --git a/include/class.topic.php b/include/class.topic.php
index ee0f2a47ede9eab61d87bf0955c5aec9fcd8bd30..1dd6d0b48f67a6fa9bb3e2c5bbf30e463337990f 100644
--- a/include/class.topic.php
+++ b/include/class.topic.php
@@ -14,6 +14,8 @@
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
 
+require_once INCLUDE_DIR . 'class.sequence.php';
+
 class Topic {
     var $id;
 
@@ -27,6 +29,8 @@ class Topic {
 
     const FORM_USE_PARENT = 4294967295;
 
+    const FLAG_CUSTOM_NUMBERS = 0x0001;
+
     function Topic($id) {
         $this->id=0;
         $this->load($id);
@@ -106,6 +110,10 @@ class Topic {
         return $this->ht['priority_id'];
     }
 
+    function getStatusId() {
+        return $this->ht['status_id'];
+    }
+
     function getStaffId() {
         return $this->ht['staff_id'];
     }
@@ -183,7 +191,28 @@ class Topic {
     }
 
     function getInfo() {
-        return $this->getHashtable();
+        $base = $this->getHashtable();
+        $base['custom-numbers'] = $this->hasFlag(self::FLAG_CUSTOM_NUMBERS);
+        return $base;
+    }
+
+    function hasFlag($flag) {
+        return $this->ht['flags'] & $flag != 0;
+    }
+
+    function getNewTicketNumber() {
+        global $cfg;
+
+        if (!$this->hasFlag(self::FLAG_CUSTOM_NUMBERS))
+            return $cfg->getNewTicketNumber();
+
+        if ($this->ht['sequence_id'])
+            $sequence = Sequence::lookup($this->ht['sequence_id']);
+        if (!$sequence)
+            $sequence = new RandomSequence();
+
+        return $sequence->next($this->ht['number_format'] ?: '######',
+            array('Ticket', 'isTicketNumberUnique'));
     }
 
     function setSortOrder($i) {
@@ -268,7 +297,7 @@ class Topic {
             if (!$disabled && $info['disabled'])
                 continue;
             if ($disabled === self::DISPLAY_DISABLED && $info['disabled'])
-                $n .= " &mdash; (disabled)";
+                $n .= " &mdash; ".__("(disabled)");
             $requested_names[$id] = $n;
         }
 
@@ -304,17 +333,21 @@ class Topic {
         $vars['topic']=Format::striptags(trim($vars['topic']));
 
         if($id && $id!=$vars['id'])
-            $errors['err']='Internal error. Try again';
+            $errors['err']=__('Internal error occurred');
 
         if(!$vars['topic'])
-            $errors['topic']='Help topic required';
+            $errors['topic']=__('Help topic name is required');
         elseif(strlen($vars['topic'])<5)
-            $errors['topic']='Topic is too short. 5 chars minimum';
+            $errors['topic']=__('Topic is too short. Five characters minimum');
         elseif(($tid=self::getIdByName($vars['topic'], $vars['topic_pid'])) && $tid!=$id)
-            $errors['topic']='Topic already exists';
+            $errors['topic']=__('Topic already exists');
 
         if (!is_numeric($vars['dept_id']))
-            $errors['dept_id']='You must select a department';
+            $errors['dept_id']=__('Department selection is required');
+
+        if ($vars['custom-numbers'] && !preg_match('`(?!<\\\)#`', $vars['number_format']))
+            $errors['number_format'] =
+                'Ticket number format requires at least one hash character (#)';
 
         if($errors) return false;
 
@@ -327,11 +360,15 @@ class Topic {
             .',topic_pid='.db_input($vars['topic_pid'])
             .',dept_id='.db_input($vars['dept_id'])
             .',priority_id='.db_input($vars['priority_id'])
+            .',status_id='.db_input($vars['status_id'])
             .',sla_id='.db_input($vars['sla_id'])
             .',form_id='.db_input($vars['form_id'])
             .',page_id='.db_input($vars['page_id'])
             .',isactive='.db_input($vars['isactive'])
             .',ispublic='.db_input($vars['ispublic'])
+            .',sequence_id='.db_input($vars['custom-numbers'] ? $vars['sequence_id'] : 0)
+            .',number_format='.db_input($vars['custom-numbers'] ? $vars['number_format'] : '')
+            .',flags='.db_input($vars['custom-numbers'] ? self::FLAG_CUSTOM_NUMBERS : 0)
             .',noautoresp='.db_input(isset($vars['noautoresp']) && $vars['noautoresp']?1:0)
             .',notes='.db_input(Format::sanitize($vars['notes']));
 
@@ -347,7 +384,8 @@ class Topic {
         if ($id) {
             $sql='UPDATE '.TOPIC_TABLE.' SET '.$sql.' WHERE topic_id='.db_input($id);
             if (!($rv = db_query($sql)))
-                $errors['err']='Unable to update topic. Internal error occurred';
+                $errors['err']=sprintf(__('Unable to update %s.'), __('this help topic'))
+                .' '.__('Internal error occurred');
         } else {
             if (isset($vars['topic_id']))
                 $sql .= ', topic_id='.db_input($vars['topic_id']);
@@ -363,7 +401,8 @@ class Topic {
             if (db_query($sql) && ($id = db_insert_id()))
                 $rv = $id;
             else
-                $errors['err']='Unable to create the topic. Internal error';
+                $errors['err']=sprintf(__('Unable to create %s.'), __('this help topic'))
+               .' '.__('Internal error occurred');
         }
         if (!$cfg || $cfg->getTopicSortMode() == 'a') {
             static::updateSortOrder();
@@ -382,6 +421,9 @@ class Topic {
         foreach ($update as $idx=>&$id) {
             $id = sprintf("(%s,%s)", db_input($id), db_input($idx+1));
         }
+        if (!count($update))
+            return;
+
         // Thanks, http://stackoverflow.com/a/3466
         $sql = sprintf('INSERT INTO `%s` (topic_id,`sort`) VALUES %s
             ON DUPLICATE KEY UPDATE `sort`=VALUES(`sort`)',
@@ -391,4 +433,4 @@ class Topic {
 }
 
 // Add fields from the standard ticket form to the ticket filterable fields
-Filter::addSupportedMatches('Help Topic', array('topicId' => 'Topic ID'), 100);
+Filter::addSupportedMatches(/* @trans */ 'Help Topic', array('topicId' => 'Topic ID'), 100);
diff --git a/include/class.translation.php b/include/class.translation.php
new file mode 100644
index 0000000000000000000000000000000000000000..cc21848a31e9153f8940c939a48d624bed36d461
--- /dev/null
+++ b/include/class.translation.php
@@ -0,0 +1,921 @@
+<?php
+/*********************************************************************
+    class.gettext.php
+
+    This implements a `Translation` class that is loosely based on the PHP
+    gettext pure-php module. It includes some code from the project and some
+    code which is based in part at least on the PHP gettext project.
+
+    This extension to the PHP gettext extension using a specially crafted MO
+    file which is a PHP hash array. The file can be built using a utility
+    method in this class.
+
+    Jared Hancock <jared@osticket.com>
+    Copyright (c)  2006-2014 osTicket
+    http://www.osticket.com
+
+    PHP gettext extension is copyrighted separately:
+    ---------------
+    Copyright (c) 2003, 2009 Danilo Segan <danilo@kvota.net>.
+    Copyright (c) 2005 Nico Kaiser <nico@siriux.net>
+
+    This file is part of PHP-gettext.
+
+    PHP-gettext is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    PHP-gettext is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with PHP-gettext; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+    ---------------
+
+    Released under the GNU General Public License WITHOUT ANY WARRANTY.
+    See LICENSE.TXT for details.
+
+    vim: expandtab sw=4 ts=4 sts=4:
+**********************************************************************/
+
+/**
+ * Provides a simple gettext replacement that works independently from
+ * the system's gettext abilities.
+ * It can read MO files and use them for translating strings.
+ * The files are passed to gettext_reader as a Stream (see streams.php)
+ *
+ * This version has the ability to cache all strings and translations to
+ * speed up the string lookup.
+ * While the cache is enabled by default, it can be switched off with the
+ * second parameter in the constructor (e.g. whenusing very large MO files
+ * that you don't want to keep in memory)
+ */
+class gettext_reader {
+  //public:
+   var $error = 0; // public variable that holds error code (0 if no error)
+
+   //private:
+  var $BYTEORDER = 0;        // 0: low endian, 1: big endian
+  var $STREAM = NULL;
+  var $short_circuit = false;
+  var $enable_cache = false;
+  var $originals = NULL;      // offset of original table
+  var $translations = NULL;    // offset of translation table
+  var $pluralheader = NULL;    // cache header field for plural forms
+  var $total = 0;          // total string count
+  var $table_originals = NULL;  // table for original strings (offsets)
+  var $table_translations = NULL;  // table for translated strings (offsets)
+  var $cache_translations = NULL;  // original -> translation mapping
+
+
+  /* Methods */
+
+
+  /**
+   * Reads a 32bit Integer from the Stream
+   *
+   * @access private
+   * @return Integer from the Stream
+   */
+  function readint() {
+      if ($this->BYTEORDER == 0) {
+        // low endian
+        $input=unpack('V', $this->STREAM->read(4));
+        return array_shift($input);
+      } else {
+        // big endian
+        $input=unpack('N', $this->STREAM->read(4));
+        return array_shift($input);
+      }
+    }
+
+  function read($bytes) {
+    return $this->STREAM->read($bytes);
+  }
+
+  /**
+   * Reads an array of Integers from the Stream
+   *
+   * @param int count How many elements should be read
+   * @return Array of Integers
+   */
+  function readintarray($count) {
+    if ($this->BYTEORDER == 0) {
+        // low endian
+        return unpack('V'.$count, $this->STREAM->read(4 * $count));
+      } else {
+        // big endian
+        return unpack('N'.$count, $this->STREAM->read(4 * $count));
+      }
+  }
+
+  /**
+   * Constructor
+   *
+   * @param object Reader the StreamReader object
+   * @param boolean enable_cache Enable or disable caching of strings (default on)
+   */
+  function gettext_reader($Reader, $enable_cache = true) {
+    // If there isn't a StreamReader, turn on short circuit mode.
+    if (! $Reader || isset($Reader->error) ) {
+      $this->short_circuit = true;
+      return;
+    }
+
+    // Caching can be turned off
+    $this->enable_cache = $enable_cache;
+
+    $MAGIC1 = "\x95\x04\x12\xde";
+    $MAGIC2 = "\xde\x12\x04\x95";
+
+    $this->STREAM = $Reader;
+    $magic = $this->read(4);
+    if ($magic == $MAGIC1) {
+      $this->BYTEORDER = 1;
+    } elseif ($magic == $MAGIC2) {
+      $this->BYTEORDER = 0;
+    } else {
+      $this->error = 1; // not MO file
+      return false;
+    }
+
+    // FIXME: Do we care about revision? We should.
+    $this->revision = $this->readint();
+
+    $this->total = $this->readint();
+    $this->originals = $this->readint();
+    $this->translations = $this->readint();
+  }
+
+  /**
+   * Loads the translation tables from the MO file into the cache
+   * If caching is enabled, also loads all strings into a cache
+   * to speed up translation lookups
+   *
+   * @access private
+   */
+  function load_tables() {
+    if (is_array($this->cache_translations) &&
+      is_array($this->table_originals) &&
+      is_array($this->table_translations))
+      return;
+
+    /* get original and translations tables */
+    if (!is_array($this->table_originals)) {
+      $this->STREAM->seekto($this->originals);
+      $this->table_originals = $this->readintarray($this->total * 2);
+    }
+    if (!is_array($this->table_translations)) {
+      $this->STREAM->seekto($this->translations);
+      $this->table_translations = $this->readintarray($this->total * 2);
+    }
+
+    if ($this->enable_cache) {
+      $this->cache_translations = array ();
+      /* read all strings in the cache */
+      for ($i = 0; $i < $this->total; $i++) {
+        $this->STREAM->seekto($this->table_originals[$i * 2 + 2]);
+        $original = $this->STREAM->read($this->table_originals[$i * 2 + 1]);
+        $this->STREAM->seekto($this->table_translations[$i * 2 + 2]);
+        $translation = $this->STREAM->read($this->table_translations[$i * 2 + 1]);
+        $this->cache_translations[$original] = $translation;
+      }
+    }
+  }
+
+  /**
+   * Returns a string from the "originals" table
+   *
+   * @access private
+   * @param int num Offset number of original string
+   * @return string Requested string if found, otherwise ''
+   */
+  function get_original_string($num) {
+    $length = $this->table_originals[$num * 2 + 1];
+    $offset = $this->table_originals[$num * 2 + 2];
+    if (! $length)
+      return '';
+    $this->STREAM->seekto($offset);
+    $data = $this->STREAM->read($length);
+    return (string)$data;
+  }
+
+  /**
+   * Returns a string from the "translations" table
+   *
+   * @access private
+   * @param int num Offset number of original string
+   * @return string Requested string if found, otherwise ''
+   */
+  function get_translation_string($num) {
+    $length = $this->table_translations[$num * 2 + 1];
+    $offset = $this->table_translations[$num * 2 + 2];
+    if (! $length)
+      return '';
+    $this->STREAM->seekto($offset);
+    $data = $this->STREAM->read($length);
+    return (string)$data;
+  }
+
+  /**
+   * Binary search for string
+   *
+   * @access private
+   * @param string string
+   * @param int start (internally used in recursive function)
+   * @param int end (internally used in recursive function)
+   * @return int string number (offset in originals table)
+   */
+  function find_string($string, $start = -1, $end = -1) {
+    if (($start == -1) or ($end == -1)) {
+      // find_string is called with only one parameter, set start end end
+      $start = 0;
+      $end = $this->total;
+    }
+    if (abs($start - $end) <= 1) {
+      // We're done, now we either found the string, or it doesn't exist
+      $txt = $this->get_original_string($start);
+      if ($string == $txt)
+        return $start;
+      else
+        return -1;
+    } else if ($start > $end) {
+      // start > end -> turn around and start over
+      return $this->find_string($string, $end, $start);
+    } else {
+      // Divide table in two parts
+      $half = (int)(($start + $end) / 2);
+      $cmp = strcmp($string, $this->get_original_string($half));
+      if ($cmp == 0)
+        // string is exactly in the middle => return it
+        return $half;
+      else if ($cmp < 0)
+        // The string is in the upper half
+        return $this->find_string($string, $start, $half);
+      else
+        // The string is in the lower half
+        return $this->find_string($string, $half, $end);
+    }
+  }
+
+  /**
+   * Translates a string
+   *
+   * @access public
+   * @param string string to be translated
+   * @return string translated string (or original, if not found)
+   */
+  function translate($string) {
+    if ($this->short_circuit)
+      return $string;
+    $this->load_tables();
+
+    if ($this->enable_cache) {
+      // Caching enabled, get translated string from cache
+      if (array_key_exists($string, $this->cache_translations))
+        return $this->cache_translations[$string];
+      else
+        return $string;
+    } else {
+      // Caching not enabled, try to find string
+      $num = $this->find_string($string);
+      if ($num == -1)
+        return $string;
+      else
+        return $this->get_translation_string($num);
+    }
+  }
+
+  /**
+   * Sanitize plural form expression for use in PHP eval call.
+   *
+   * @access private
+   * @return string sanitized plural form expression
+   */
+  function sanitize_plural_expression($expr) {
+    // Get rid of disallowed characters.
+    $expr = preg_replace('@[^a-zA-Z0-9_:;\(\)\?\|\&=!<>+*/\%-]@', '', $expr);
+
+    // Add parenthesis for tertiary '?' operator.
+    $expr .= ';';
+    $res = '';
+    $p = 0;
+    for ($i = 0, $k = strlen($expr); $i < $k; $i++) {
+      $ch = $expr[$i];
+      switch ($ch) {
+      case '?':
+        $res .= ' ? (';
+        $p++;
+        break;
+      case ':':
+        $res .= ') : (';
+        break;
+      case ';':
+        $res .= str_repeat( ')', $p) . ';';
+        $p = 0;
+        break;
+      default:
+        $res .= $ch;
+      }
+    }
+    return $res;
+  }
+
+  /**
+   * Parse full PO header and extract only plural forms line.
+   *
+   * @access private
+   * @return string verbatim plural form header field
+   */
+  function extract_plural_forms_header_from_po_header($header) {
+    $regs = array();
+    if (preg_match("/(^|\n)plural-forms: ([^\n]*)\n/i", $header, $regs))
+      $expr = $regs[2];
+    else
+      $expr = "nplurals=2; plural=n == 1 ? 0 : 1;";
+    return $expr;
+  }
+
+  /**
+   * Get possible plural forms from MO header
+   *
+   * @access private
+   * @return string plural form header
+   */
+  function get_plural_forms() {
+    // lets assume message number 0 is header
+    // this is true, right?
+    $this->load_tables();
+
+    // cache header field for plural forms
+    if (! is_string($this->pluralheader)) {
+      if ($this->enable_cache) {
+        $header = $this->cache_translations[""];
+      } else {
+        $header = $this->get_translation_string(0);
+      }
+      $expr = $this->extract_plural_forms_header_from_po_header($header);
+      $this->pluralheader = $this->sanitize_plural_expression($expr);
+    }
+    return $this->pluralheader;
+  }
+
+  /**
+   * Detects which plural form to take
+   *
+   * @access private
+   * @param n count
+   * @return int array index of the right plural form
+   */
+  function select_string($n) {
+      // Expression reads
+      // nplurals=X; plural= n != 1
+      if (!isset($this->plural_expression)) {
+          $matches = array();
+          if (!preg_match('`nplurals\s*=\s*(\d+)\s*;\s*plural\s*=\s*(.+$)`',
+                  $this->get_plural_forms(), $matches))
+              return 1;
+
+          $this->plural_expression = create_function('$n',
+              sprintf('return %s;', str_replace('n', '($n)', $matches[2])));
+          $this->plural_total = (int) $matches[1];
+      }
+      $func = $this->plural_expression;
+      $plural = $func($n);
+      return ($plural > $this->plural_total)
+          ? $this->plural_total - 1
+          : $plural;
+  }
+
+  /**
+   * Plural version of gettext
+   *
+   * @access public
+   * @param string single
+   * @param string plural
+   * @param string number
+   * @return translated plural form
+   */
+  function ngettext($single, $plural, $number) {
+    if ($this->short_circuit) {
+      if ($number != 1)
+        return $plural;
+      else
+        return $single;
+    }
+
+    // find out the appropriate form
+    $select = $this->select_string($number);
+
+    // this should contains all strings separated by NULLs
+    $key = $single . chr(0) . $plural;
+
+
+    if ($this->enable_cache) {
+      if (! array_key_exists($key, $this->cache_translations)) {
+        return ($number != 1) ? $plural : $single;
+      } else {
+        $result = $this->cache_translations[$key];
+        $list = explode(chr(0), $result);
+        return $list[$select];
+      }
+    } else {
+      $num = $this->find_string($key);
+      if ($num == -1) {
+        return ($number != 1) ? $plural : $single;
+      } else {
+        $result = $this->get_translation_string($num);
+        $list = explode(chr(0), $result);
+        return $list[$select];
+      }
+    }
+  }
+
+  function pgettext($context, $msgid) {
+    $key = $context . chr(4) . $msgid;
+    $ret = $this->translate($key);
+    if (strpos($ret, "\004") !== FALSE) {
+      return $msgid;
+    } else {
+      return $ret;
+    }
+  }
+
+  function npgettext($context, $singular, $plural, $number) {
+    $key = $context . chr(4) . $singular;
+    $ret = $this->ngettext($key, $plural, $number);
+    if (strpos($ret, "\004") !== FALSE) {
+      return $singular;
+    } else {
+      return $ret;
+    }
+
+  }
+}
+
+class FileReader {
+  var $_pos;
+  var $_fd;
+  var $_length;
+
+  function FileReader($filename) {
+    if (is_resource($filename)) {
+        $this->_length = strlen(stream_get_contents($filename));
+        rewind($filename);
+        $this->_fd = $filename;
+    }
+    elseif (file_exists($filename)) {
+
+      $this->_length=filesize($filename);
+      $this->_fd = fopen($filename,'rb');
+      if (!$this->_fd) {
+        $this->error = 3; // Cannot read file, probably permissions
+        return false;
+      }
+    } else {
+      $this->error = 2; // File doesn't exist
+      return false;
+    }
+    $this->_pos = 0;
+  }
+
+  function read($bytes) {
+    if ($bytes) {
+      fseek($this->_fd, $this->_pos);
+
+      // PHP 5.1.1 does not read more than 8192 bytes in one fread()
+      // the discussions at PHP Bugs suggest it's the intended behaviour
+      $data = '';
+      while ($bytes > 0) {
+        $chunk  = fread($this->_fd, $bytes);
+        $data  .= $chunk;
+        $bytes -= strlen($chunk);
+      }
+      $this->_pos = ftell($this->_fd);
+
+      return $data;
+    } else return '';
+  }
+
+  function seekto($pos) {
+    fseek($this->_fd, $pos);
+    $this->_pos = ftell($this->_fd);
+    return $this->_pos;
+  }
+
+  function currentpos() {
+    return $this->_pos;
+  }
+
+  function length() {
+    return $this->_length;
+  }
+
+  function close() {
+    fclose($this->_fd);
+  }
+
+}
+
+/**
+ * Class: Translation
+ *
+ * This class is strongly based on the gettext_reader class. It makes use of
+ * a few simple optimizations for the context of osTicket
+ *
+ *    * The language packs are pre-compiled and distributed (which means
+ *      they can be customized).
+ *    * The MO file will always be processed by PHP code
+ *    * osTicket uses utf-8 output exclusively (for web traffic anyway)
+ *
+ * These allow us to optimize the MO file for the osTicket project
+ * specifically and make enough of an optimization to allow using a pure-PHP
+ * source gettext library implementation which should be roughly the same
+ * performance as the libc gettext library.
+ */
+class Translation extends gettext_reader implements Serializable {
+
+    var $charset;
+
+    const META_HEADER = 0;
+
+    function __construct($reader, $charset=false) {
+        if (!$reader)
+            return $this->short_circuit = true;
+
+        // Just load the cache
+        if (!is_string($reader))
+            throw new RuntimeException('Programming Error: Expected filename for translation source');
+        $this->STREAM = $reader;
+
+        $this->enable_cache = true;
+        $this->charset = $charset;
+        $this->encode = $charset && strcasecmp($charset, 'utf-8') !== 0;
+        $this->load_tables();
+    }
+
+    function load_tables() {
+        if (isset($this->cache_translations))
+            return;
+
+        $this->cache_translations = (include $this->STREAM);
+    }
+
+    function translate($string) {
+        if ($this->short_circuit)
+            return $string;
+
+        // Caching enabled, get translated string from cache
+        if (isset($this->cache_translations[$string]))
+            $string = $this->cache_translations[$string];
+
+        if (!$this->encode)
+            return $string;
+
+        return Format::encode($string, 'utf-8', $this->charset);
+    }
+
+    static function buildHashFile($mofile, $outfile=false, $return=false) {
+        if (!$outfile) {
+            $stream = fopen('php://stdout', 'w');
+        }
+        elseif (is_string($outfile)) {
+            $stream = fopen($outfile, 'w');
+        }
+        elseif (is_resource($outfile)) {
+            $stream = $outfile;
+        }
+
+        if (!$stream)
+            throw new InvalidArgumentException(
+                'Expected a filename or valid resource');
+
+        if (!$mofile instanceof FileReader)
+            $mofile = new FileReader($mofile);
+
+        $reader = new parent($mofile, true);
+
+        if ($reader->short_circuit || $reader->error)
+            throw new Exception('Unable to initialize MO input file');
+
+        $reader->load_tables();
+
+        // Get basic table
+        if (!($table = $reader->cache_translations))
+            throw new Exception('Unable to read translations from file');
+
+        // Transcode the table to UTF-8
+        $header = $table[""];
+        $info = array();
+        preg_match('/^content-type: (.*)$/im', $header, $info);
+        $charset = false;
+        if ($content_type = $info[1]) {
+            // Find the charset property
+            $settings = explode(';', $content_type);
+            foreach ($settings as $v) {
+                @list($prop, $value) = explode('=', trim($v), 2);
+                if (strtolower($prop) == 'charset') {
+                    $charset = trim($value);
+                    break;
+                }
+            }
+        }
+        if ($charset && strcasecmp($charset, 'utf-8') !== 0) {
+            foreach ($table as $orig=>$trans) {
+                // Format::encode defaults to UTF-8 output
+                $table[Format::encode($orig, $charset)] =
+                    Format::encode($trans, $charset);
+                unset($table[$orig]);
+            }
+        }
+
+        // Add in some meta-data
+        $table[self::META_HEADER] = array(
+            'Revision' => $reader->revision,      // From the MO
+            'Total-Strings' => $reader->total,    // From the MO
+            'Table-Size' => count($table),      // Sanity check for later
+            'Build-Timestamp' => gmdate(DATE_RFC822),
+            'Format-Version' => 'A',            // Support future formats
+            'Encoding' => 'UTF-8',
+        );
+
+        // Serialize the PHP array and write to output
+        $contents = sprintf('<?php return %s;', var_export($table, true));
+        if ($return)
+            return $contents;
+        else
+            fwrite($stream, $contents);
+    }
+
+    static function resurrect($key) {
+        if (!function_exists('apc_fetch'))
+            return false;
+
+        $success = true;
+        if (($translation = apc_fetch($key, $success)) && $success)
+            return $translation;
+    }
+    function cache($key) {
+        if (function_exists('apc_add'))
+            apc_add($key, $this);
+    }
+
+
+    function serialize() {
+        return serialize(array($this->charset, $this->encode, $this->cache_translations));
+    }
+    function unserialize($what) {
+        list($this->charset, $this->encode, $this->cache_translations)
+            = unserialize($what);
+        $this->short_circuit = ! $this->enable_cache
+            = 0 < count($this->cache_translations);
+    }
+}
+
+if (!defined('LC_MESSAGES')) {
+    define('LC_ALL', 0);
+    define('LC_CTYPE', 1);
+    define('LC_NUMERIC', 2);
+    define('LC_TIME', 3);
+    define('LC_COLLATE', 4);
+    define('LC_MONETARY', 5);
+    define('LC_MESSAGES', 6);
+}
+
+class TextDomain {
+    var $l10n = array();
+    var $path;
+    var $codeset;
+    var $domain;
+
+    static $registry;
+    static $default_domain = 'messages';
+    static $current_locale = '';
+    static $LC_CATEGORIES = array(
+        LC_ALL => 'LC_ALL',
+        LC_CTYPE => 'LC_CTYPE',
+        LC_NUMERIC => 'LC_NUMERIC',
+        LC_TIME => 'LC_TIME',
+        LC_COLLATE => 'LC_COLLATE',
+        LC_MONETARY => 'LC_MONETARY',
+        LC_MESSAGES => 'LC_MESSAGES'
+    );
+
+    function __construct($domain) {
+        $this->domain = $domain;
+    }
+
+    function getTranslation($category=LC_MESSAGES, $locale=false) {
+        $locale = $locale ?: self::$current_locale
+            ?: self::setLocale(LC_MESSAGES, 0);
+
+        if (isset($this->l10n[$locale]))
+            return $this->l10n[$locale];
+
+        if ($locale == 'en_US') {
+            $this->l10n[$locale] = new Translation(null);
+        }
+        else {
+            // get the current locale
+            $bound_path = @$this->path ?: './';
+            $subpath = self::$LC_CATEGORIES[$category] .
+                '/'.$this->domain.'.mo.php';
+
+            // APC short-circuit (if supported)
+            $key = sha1($locale .':lang:'. $subpath);
+            if ($T = Translation::resurrect($key)) {
+                return $this->l10n[$locale] = $T;
+            }
+
+            $locale_names = self::get_list_of_locales($locale);
+            $input = null;
+            foreach ($locale_names as $T) {
+                if (substr($bound_path, 7) != 'phar://') {
+                    $phar_path = 'phar://' . $bound_path . $T . ".phar/" . $subpath;
+                    if (file_exists($phar_path)) {
+                        $input = $phar_path;
+                        break;
+                    }
+                }
+                $full_path = $bound_path . $T . "/" . $subpath;
+                if (file_exists($full_path)) {
+                    $input = $full_path;
+                    break;
+                }
+            }
+            // TODO: Handle charset hint from the environment
+            $this->l10n[$locale] = $T = new Translation($input);
+            $T->cache($key);
+        }
+        return $this->l10n[$locale];
+    }
+
+    function setPath($path) {
+        $this->path = $path;
+    }
+
+    static function configureForUser($user=false) {
+        $lang = Internationalization::getCurrentLanguage($user);
+
+        $info = Internationalization::getLanguageInfo(strtolower($lang));
+        if (!$info)
+            // Not a supported language
+            return;
+
+        // Define locale for C-libraries
+        putenv('LC_ALL=' . $info['code']);
+        self::setLocale(LC_ALL, $info['code']);
+    }
+
+    static function setDefaultDomain($domain) {
+        static::$default_domain = $domain;
+    }
+
+    /**
+     * Returns passed in $locale, or environment variable $LANG if $locale == ''.
+     */
+    static function get_default_locale($locale='') {
+        if ($locale == '') // emulate variable support
+            return getenv('LANG');
+        else
+            return $locale;
+    }
+
+    static function get_list_of_locales($locale) {
+        /* Figure out all possible locale names and start with the most
+         * specific ones.  I.e. for sr_CS.UTF-8@latin, look through all of
+         * sr_CS.UTF-8@latin, sr_CS@latin, sr@latin, sr_CS.UTF-8, sr_CS, sr.
+        */
+        $locale_names = $m = array();
+        $lang = null;
+        if ($locale) {
+            if (preg_match("/^(?P<lang>[a-z]{2,3})"              // language code
+                ."(?:_(?P<country>[A-Z]{2}))?"           // country code
+                ."(?:\.(?P<charset>[-A-Za-z0-9_]+))?"    // charset
+                ."(?:@(?P<modifier>[-A-Za-z0-9_]+))?$/",  // @ modifier
+                $locale, $m)
+            ) {
+
+            if ($m['modifier']) {
+                // TODO: Confirm if Crowdin uses the modifer flags
+                if ($m['country']) {
+                    $locale_names[] = "{$m['lang']}_{$m['country']}@{$m['modifier']}";
+                }
+                $locale_names[] = "{$m['lang']}@{$m['modifier']}";
+            }
+            if ($m['country']) {
+                $locale_names[] = "{$m['lang']}_{$m['country']}";
+            }
+            $locale_names[] = $m['lang'];
+        }
+
+        // If the locale name doesn't match POSIX style, just include it as-is.
+        if (!in_array($locale, $locale_names))
+            $locale_names[] = $locale;
+      }
+      return array_filter($locale_names);
+    }
+
+    static function setLocale($category, $locale) {
+        if ($locale === 0) { // use === to differentiate between string "0"
+            if (self::$current_locale != '')
+                return self::$current_locale;
+            else
+                // obey LANG variable, maybe extend to support all of LC_* vars
+                // even if we tried to read locale without setting it first
+                return self::setLocale($category, self::$current_locale);
+        } else {
+            if (function_exists('setlocale')) {
+              $ret = setlocale($category, $locale);
+              if (($locale == '' and !$ret) or // failed setting it by env
+                  ($locale != '' and $ret != $locale)) { // failed setting it
+                // Failed setting it according to environment.
+                self::$current_locale = self::get_default_locale($locale);
+              } else {
+                self::$current_locale = $ret;
+              }
+            } else {
+              // No function setlocale(), emulate it all.
+              self::$current_locale = self::get_default_locale($locale);
+            }
+            return self::$current_locale;
+        }
+    }
+
+    static function lookup($domain=null) {
+        if (!isset($domain))
+            $domain = self::$default_domain;
+        if (!isset(static::$registry[$domain])) {
+            static::$registry[$domain] = new TextDomain($domain);
+        }
+        return static::$registry[$domain];
+    }
+}
+
+// Functions for gettext library. Since the gettext extension for PHP is not
+// used as a fallback, there is no detection and compat funciton
+// installation for the gettext library function calls.
+
+function _gettext($msgid) {
+    return TextDomain::lookup()->getTranslation()->translate($msgid);
+}
+function __($msgid) {
+    return _gettext($msgid);
+}
+function _ngettext($singular, $plural, $number) {
+    return TextDomain::lookup()->getTranslation()
+        ->ngettext($singular, $plural, $number);
+}
+function _dgettext($domain, $msgid) {
+    return TextDomain::lookup($domain)->getTranslation()
+        ->translate($msgid);
+}
+function _dngettext($domain, $singular, $plural, $number) {
+    return TextDomain::lookup($domain)->getTranslation()
+        ->ngettext($singular, $plural, $number);
+}
+function _dcgettext($domain, $msgid, $category) {
+    return TextDomain::lookup($domain)->getTranslation($category)
+        ->translate($msgid);
+}
+function _dcngettext($domain, $singular, $plural, $number, $category) {
+    return TextDomain::lookup($domain)->getTranslation($category)
+        ->ngettext($singular, $plural, $number);
+}
+function _pgettext($context, $msgid) {
+    return TextDomain::lookup()->getTranslation()
+        ->pgettext($context, $msgid);
+}
+function _dpgettext($domain, $context, $msgid) {
+    return TextDomain::lookup($domain)->getTranslation()
+        ->pgettext($context, $msgid);
+}
+function _dcpgettext($domain, $context, $msgid, $category) {
+    return TextDomain::lookup($domain)->getTranslation($category)
+        ->pgettext($context, $msgid);
+}
+function _npgettext($context, $singular, $plural, $n) {
+    return TextDomain::lookup()->getTranslation()
+        ->npgettext($context, $singular, $plural, $n);
+}
+function _dnpgettext($domain, $context, $singular, $plural, $n) {
+    return TextDomain::lookup($domain)->getTranslation()
+        ->npgettext($context, $singular, $plural, $n);
+}
+function _dcnpgettext($domain, $context, $singular, $plural, $category, $n) {
+    return TextDomain::lookup($domain)->getTranslation($category)
+        ->npgettext($context, $singular, $plural, $n);
+}
+
+
+do {
+  if (PHP_SAPI != 'cli') break;
+  if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break;
+  if (empty ($_SERVER['PHP_SELF']) || FALSE === strpos ($_SERVER['PHP_SELF'], basename(__FILE__)) ) break;
+  $file = $argv[1];
+  Translation::buildHashFile($file);
+} while (0);
diff --git a/include/class.upgrader.php b/include/class.upgrader.php
index c08ff6c43cbbd4e9e347efc27108873d1695e0c5..6246e542d0b524b3dec26364297a506d9aab18ce 100644
--- a/include/class.upgrader.php
+++ b/include/class.upgrader.php
@@ -215,7 +215,7 @@ class StreamUpgrader extends SetupWizard {
     function onError($error) {
         global $ost, $thisstaff;
 
-        $subject = '['.$this->name.']: Upgrader Error';
+        $subject = '['.$this->name.']: '._S('Upgrader Error');
         $ost->logError($subject, $error);
         $this->setError($error);
         $this->upgrader->setState('aborted');
@@ -232,7 +232,8 @@ class StreamUpgrader extends SetupWizard {
         if($email) {
             $email->sendAlert($thisstaff->getEmail(), $subject, $error);
         } else {//no luck - try the system mail.
-            Mailer::sendmail($thisstaff->getEmail(), $subject, $error, sprintf('"osTicket Alerts"<%s>', $thisstaff->getEmail()));
+            Mailer::sendmail($thisstaff->getEmail(), $subject, $error,
+                '"'._S('osTicket Alerts')."\" <{$thisstaff->getEmail()}>");
         }
 
     }
@@ -278,7 +279,7 @@ class StreamUpgrader extends SetupWizard {
 
     function getNextVersion() {
         if(!$patch=$this->getNextPatch())
-            return '(Latest)';
+            return __('(Latest)');
 
         $info = $this->readPatchInfo($patch);
         return $info['version'];
@@ -318,11 +319,11 @@ class StreamUpgrader extends SetupWizard {
 
     function getNextAction() {
 
-        $action='Upgrade osTicket to '.$this->getVersion();
+        $action=sprintf(__('Upgrade osTicket to %s'), $this->getVersion());
         if($task=$this->getTask()) {
             $action = $task->getDescription() .' ('.$task->getStatus().')';
         } elseif($this->isUpgradable() && ($nextversion = $this->getNextVersion())) {
-            $action = "Upgrade to $nextversion";
+            $action = sprintf(__("Upgrade to %s"),$nextversion);
         }
 
         return '['.$this->name.'] '.$action;
@@ -361,8 +362,8 @@ class StreamUpgrader extends SetupWizard {
             return false; //Nothing to do.
 
         $this->log(
-                sprintf('Upgrader - %s (task pending).', $this->getShash()),
-                sprintf('The %s task reports there is work to do',
+                sprintf(_S('Upgrader - %s (task pending).'), $this->getShash()),
+                sprintf(_S('The %s task reports there is work to do'),
                     get_class($task))
                 );
         if(!($max_time = ini_get('max_execution_time')))
@@ -405,11 +406,11 @@ class StreamUpgrader extends SetupWizard {
             $shash = substr($phash, 9, 8);
 
             //Log the patch info
-            $logMsg = "Patch $phash applied successfully ";
+            $logMsg = sprintf(_S("Patch %s applied successfully"), $phash);
             if(($info = $this->readPatchInfo($patch)) && $info['version'])
                 $logMsg.= ' ('.$info['version'].') ';
 
-            $this->log("Upgrader - $shash applied", $logMsg);
+            $this->log(sprintf(_S("Upgrader - %s applied"), $shash), $logMsg);
             $this->signature = $shash; //Update signature to the *new* HEAD
             $this->phash = $phash;
 
@@ -450,12 +451,12 @@ class StreamUpgrader extends SetupWizard {
 
         //We have a cleanup script  ::XXX: Don't abort on error?
         if($this->load_sql_file($file, $this->getTablePrefix(), false, true)) {
-            $this->log("Upgrader - {$this->phash} cleanup",
-                "Applied cleanup script {$file}");
+            $this->log(sprintf(_S("Upgrader - %s cleanup"), $this->phash),
+                sprintf(_S("Applied cleanup script %s"), $file));
             return 0;
         }
 
-        $this->log('Upgrader', sprintf("%s: Unable to process cleanup file",
+        $this->log(_S('Upgrader'), sprintf(_S("%s: Unable to process cleanup file"),
                         $this->phash));
         return 0;
     }
diff --git a/include/class.user.php b/include/class.user.php
index f660fccf918eb4cfcaee16c68fc3603be48ed4f7..4fec3df6275ebbed48f6758be6cb9e5944435304 100644
--- a/include/class.user.php
+++ b/include/class.user.php
@@ -14,7 +14,8 @@
 
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
-require_once(INCLUDE_DIR . 'class.orm.php');
+require_once INCLUDE_DIR . 'class.orm.php';
+require_once INCLUDE_DIR . 'class.util.php';
 
 class UserEmailModel extends VerySimpleModel {
     static $meta = array(
@@ -26,6 +27,10 @@ class UserEmailModel extends VerySimpleModel {
             )
         )
     );
+
+    function __toString() {
+        return $this->address;
+    }
 }
 
 class TicketModel extends VerySimpleModel {
@@ -35,6 +40,9 @@ class TicketModel extends VerySimpleModel {
         'joins' => array(
             'user' => array(
                 'constraint' => array('user_id' => 'UserModel.id')
+            ),
+            'status' => array(
+                'constraint' => array('status_id' => 'TicketStatus.id')
             )
         )
     );
@@ -186,19 +194,23 @@ class User extends UserModel {
     }
 
     static function fromForm($form) {
+        global $thisstaff;
 
         if(!$form) return null;
 
         //Validate the form
         $valid = true;
-        if (!$form->isValid())
+        $filter = function($f) use ($thisstaff) {
+            return !isset($thisstaff) || $f->isRequiredForStaff();
+        };
+        if (!$form->isValid($filter))
             $valid  = false;
 
         //Make sure the email is not in-use
         if (($field=$form->getField('email'))
                 && $field->getClean()
                 && User::lookup(array('emails__address'=>$field->getClean()))) {
-            $field->addError('Email is assigned to another user');
+            $field->addError(__('Email is assigned to another user'));
             $valid = false;
         }
 
@@ -254,6 +266,10 @@ class User extends UserModel {
         return JsonDataEncoder::encode($info);
     }
 
+    function __toString() {
+        return $this->asVar();
+    }
+
     function asVar() {
         return (string) $this->getName();
     }
@@ -279,10 +295,10 @@ class User extends UserModel {
         return $uf;
     }
 
-    function getDynamicData() {
+    function getDynamicData($create=true) {
         if (!isset($this->_entries)) {
             $this->_entries = DynamicFormEntry::forClient($this->id)->all();
-            if (!$this->_entries) {
+            if (!$this->_entries && $create) {
                 $g = UserForm::getNewInstance();
                 $g->setClientId($this->id);
                 $g->save();
@@ -290,7 +306,7 @@ class User extends UserModel {
             }
         }
 
-        return $this->_entries;
+        return $this->_entries ?: array();
     }
 
     function getFilterData() {
@@ -335,7 +351,7 @@ class User extends UserModel {
     function getAccountStatus() {
 
         if (!($account=$this->getAccount()))
-            return 'Guest';
+            return __('Guest');
 
         return (string) $account->getStatus();
     }
@@ -351,7 +367,7 @@ class User extends UserModel {
 
     static function importCsv($stream, $defaults=array()) {
         //Read the header (if any)
-        $headers = array('name' => 'Full Name', 'email' => 'Email Address');
+        $headers = array('name' => __('Full Name'), 'email' => __('Email Address'));
         $uform = UserForm::getUserForm();
         $all_fields = $uform->getFields();
         $named_fields = array();
@@ -361,7 +377,7 @@ class User extends UserModel {
                 $named_fields[] = $f;
 
         if (!($data = fgetcsv($stream, 1000, ",")))
-            return 'Whoops. Perhaps you meant to send some CSV records';
+            return __('Whoops. Perhaps you meant to send some CSV records');
 
         if (Validator::is_email($data[1])) {
             $has_header = false; // We don't have an header!
@@ -375,7 +391,8 @@ class User extends UserModel {
                             mb_strtolower($f->get('name')), mb_strtolower($f->get('label'))))) {
                         $found = true;
                         if (!$f->get('name'))
-                            return $h.': Field must have `variable` set to be imported';
+                            return sprintf(__(
+                                '%s: Field must have `variable` set to be imported'), $h);
                         $headers[$f->get('name')] = $f->get('label');
                         break;
                     }
@@ -391,7 +408,7 @@ class User extends UserModel {
                         break;
                     }
                     else {
-                        return $h.': Unable to map header to a user field';
+                        return sprintf(__('%s: Unable to map header to a user field'), $h);
                     }
                 }
             }
@@ -399,7 +416,7 @@ class User extends UserModel {
 
         // 'name' and 'email' MUST be in the headers
         if (!isset($headers['name']) || !isset($headers['email']))
-            return 'CSV file must include `name` and `email` columns';
+            return __('CSV file must include `name` and `email` columns');
 
         if (!$has_header)
             fseek($stream, 0);
@@ -426,14 +443,17 @@ class User extends UserModel {
                 // Skip empty rows
                 continue;
             elseif (count($data) != count($headers))
-                return 'Bad data. Expected: '.implode(', ', $headers);
+                return sprintf(__('Bad data. Expected: %s'), implode(', ', $headers));
             // Validate according to field configuration
             $i = 0;
             foreach ($headers as $h => $label) {
                 $f = $fields[$h];
                 $T = $f->parse($data[$i]);
                 if ($f->validateEntry($T) && $f->errors())
-                    return $label.': Invalid data: '.implode(', ', $f->errors());
+                    return sprintf(__(
+                        /* 1 will be a field label, and 2 will be error messages */
+                        '%1$s: Invalid data: %2$s'),
+                        $label, implode(', ', $f->errors()));
                 // Convert to database format
                 $data[$i] = $f->to_database($T);
                 $i++;
@@ -448,7 +468,8 @@ class User extends UserModel {
         foreach ($users as $u) {
             $vars = array_combine($keys, $u);
             if (!static::fromVars($vars))
-                return 'Unable to import user: '.print_r($vars, true);
+                return sprintf(__('Unable to import user: %s'),
+                    print_r($vars, true));
         }
 
         return count($users);
@@ -466,19 +487,21 @@ class User extends UserModel {
             rewind($stream);
         }
         else {
-            return 'Unable to parse submitted users';
+            return __('Unable to parse submitted users');
         }
 
         return User::importCsv($stream, $extra);
     }
 
-    function updateInfo($vars, &$errors) {
+    function updateInfo($vars, &$errors, $staff=false) {
 
         $valid = true;
         $forms = $this->getDynamicData();
         foreach ($forms as $cd) {
             $cd->setSource($vars);
-            if (!$cd->isValidForClient())
+            if ($staff && !$cd->isValidForStaff())
+                $valid = false;
+            elseif (!$cd->isValidForClient())
                 $valid = false;
             elseif ($cd->get('type') == 'U'
                         && ($form= $cd->getForm())
@@ -487,7 +510,7 @@ class User extends UserModel {
                         && ($u=User::lookup(array('emails__address'=>$f->getClean())))
                         && $u->id != $this->getId()) {
                 $valid = false;
-                $f->addError('Email is assigned to another user');
+                $f->addError(__('Email is assigned to another user'));
             }
         }
 
@@ -570,16 +593,16 @@ class PersonsName {
     var $name;
 
     static $formats = array(
-        'first' => array("First", 'getFirst'),
-        'last' => array("Last", 'getLast'),
-        'full' => array("First Last", 'getFull'),
-        'legal' => array("First M. Last", 'getLegal'),
-        'lastfirst' => array("Last, First", 'getLastFirst'),
-        'formal' => array("Mr. Last", 'getFormal'),
-        'short' => array("First L.", 'getShort'),
-        'shortformal' => array("F. Last", 'getShortFormal'),
-        'complete' => array("Mr. First M. Last Sr.", 'getComplete'),
-        'original' => array('-- As Entered --', 'getOriginal'),
+        'first' => array(     /*@trans*/ "First", 'getFirst'),
+        'last' => array(      /*@trans*/ "Last", 'getLast'),
+        'full' => array(      /*@trans*/ "First Last", 'getFull'),
+        'legal' => array(     /*@trans*/ "First M. Last", 'getLegal'),
+        'lastfirst' => array( /*@trans*/ "Last, First", 'getLastFirst'),
+        'formal' => array(    /*@trans*/ "Mr. Last", 'getFormal'),
+        'short' => array(     /*@trans*/ "First L.", 'getShort'),
+        'shortformal' => array(/*@trans*/ "F. Last", 'getShortFormal'),
+        'complete' => array(  /*@trans*/ "Mr. First M. Last Sr.", 'getComplete'),
+        'original' => array(  /*@trans*/ '-- As Entered --', 'getOriginal'),
     );
 
     function __construct($name, $format=null) {
@@ -844,6 +867,10 @@ class UserAccountModel extends VerySimpleModel {
         $this->user->set('account', $this);
         return $this->user;
     }
+
+    function getLanguage() {
+        return $this->get('lang');
+    }
 }
 
 class UserAccount extends UserAccountModel {
@@ -869,7 +896,8 @@ class UserAccount extends UserAccountModel {
         $content = Page::lookup(Page::getIdByType($template));
 
         if (!$email ||  !$content)
-            return new Error($template.': Unable to retrieve template');
+            return new Error(sprintf(_S('%s: Unable to retrieve template'),
+                $template));
 
         $vars = array(
             'url' => $ost->getConfig()->getBaseUrl(),
@@ -912,34 +940,34 @@ class UserAccount extends UserAccountModel {
 
 
         if (!$thisstaff) {
-            $errors['err'] = 'Access Denied';
+            $errors['err'] = __('Access Denied');
             return false;
         }
 
         // TODO: Make sure the username is unique
 
         if (!$vars['timezone_id'])
-            $errors['timezone_id'] = 'Time zone required';
+            $errors['timezone_id'] = __('Time zone selection is required');
 
         // Changing password?
         if ($vars['passwd1'] || $vars['passwd2']) {
             if (!$vars['passwd1'])
-                $errors['passwd1'] = 'New password required';
+                $errors['passwd1'] = __('New password is required');
             elseif ($vars['passwd1'] && strlen($vars['passwd1'])<6)
-                $errors['passwd1'] = 'Must be at least 6 characters';
+                $errors['passwd1'] = __('Must be at least 6 characters');
             elseif ($vars['passwd1'] && strcmp($vars['passwd1'], $vars['passwd2']))
-                $errors['passwd2'] = 'Password(s) do not match';
+                $errors['passwd2'] = __('Passwords do not match');
         }
 
+        // Make sure the username is not an email.
+        if ($vars['username'] && Validator::is_email($vars['username']))
+            $errors['username'] =
+                __('Users can always sign in with their email address');
+
         if ($errors) return false;
 
         $this->set('timezone_id', $vars['timezone_id']);
         $this->set('dst', isset($vars['dst']) ? 1 : 0);
-
-        // Make sure the username is not an email.
-        if ($vars['username'] && Validator::is_email($vars['username']))
-             $vars['username'] = '';
-
         $this->set('username', $vars['username']);
 
         if ($vars['passwd1']) {
@@ -989,11 +1017,11 @@ class UserAccount extends UserAccountModel {
         if ((!$vars['backend'] || $vars['backend'] != 'client')
                 && !isset($vars['sendemail'])) {
             if (!$vars['passwd1'])
-                $errors['passwd1'] = 'Temp. password required';
+                $errors['passwd1'] = 'Temporary password required';
             elseif ($vars['passwd1'] && strlen($vars['passwd1'])<6)
                 $errors['passwd1'] = 'Must be at least 6 characters';
             elseif ($vars['passwd1'] && strcmp($vars['passwd1'], $vars['passwd2']))
-                $errors['passwd2'] = 'Password(s) do not match';
+                $errors['passwd2'] = 'Passwords do not match';
         }
 
         if ($errors) return false;
@@ -1060,14 +1088,14 @@ class UserAccountStatus {
     function __toString() {
 
         if ($this->isLocked())
-            return 'Locked (Administrative)';
+            return __('Locked (Administrative)');
 
         if (!$this->isConfirmed())
-            return 'Locked (Pending Activation)';
+            return __('Locked (Pending Activation)');
 
         // ... Other flags here (password reset, etc).
 
-        return 'Active (Registered)';
+        return __('Active (Registered)');
     }
 }
 
@@ -1075,45 +1103,12 @@ class UserAccountStatus {
 /*
  *  Generic user list.
  */
-class UserList implements  IteratorAggregate, ArrayAccess {
-    private $users;
-
-    function __construct($list = array()) {
-        $this->users = $list;
-    }
-
-    function add($user) {
-        $this->offsetSet(null, $user);
-    }
-
-    function offsetSet($offset, $value) {
-
-        if (is_null($offset))
-            $this->users[] = $value;
-        else
-            $this->users[$offset] = $value;
-    }
-
-    function offsetExists($offset) {
-        return isset($this->users[$offset]);
-    }
-
-    function offsetUnset($offset) {
-        unset($this->users[$offset]);
-    }
-
-    function offsetGet($offset) {
-        return isset($this->users[$offset]) ? $this->users[$offset] : null;
-    }
-
-    function getIterator() {
-        return new ArrayIterator($this->users);
-    }
+class UserList extends ListObject {
 
     function __toString() {
 
         $list = array();
-        foreach($this->users as $user) {
+        foreach($this->storage as $user) {
             if (is_object($user))
                 $list [] = $user->getName();
         }
@@ -1121,6 +1116,7 @@ class UserList implements  IteratorAggregate, ArrayAccess {
         return $list ? implode(', ', $list) : '';
     }
 }
+
 require_once(INCLUDE_DIR . 'class.organization.php');
 User::_inspect();
 UserAccount::_inspect();
diff --git a/include/class.util.php b/include/class.util.php
new file mode 100644
index 0000000000000000000000000000000000000000..8fb9b3967a4ec5cb4e6d2117c58ece03baa1b0fd
--- /dev/null
+++ b/include/class.util.php
@@ -0,0 +1,169 @@
+<?php
+/**
+ * Jared Hancock <jared@osticket.com>
+ * Copyright (c)  2014
+ *
+ * Lightweight implementation of the Python list in PHP. This allows for
+ * treating an array like a simple list of items. The numeric indexes are
+ * automatically updated so that the indeces of the list will alway be from
+ * zero and increasing positively.
+ *
+ * Negative indexes are supported which reference from the end of the list.
+ * Therefore $queue[-1] will refer to the last item in the list.
+ */
+class ListObject implements IteratorAggregate, ArrayAccess, Serializable, Countable {
+
+    protected $storage = array();
+
+    function __construct(array $array=array()) {
+        foreach ($array as $v)
+            $this->storage[] = $v;
+    }
+
+    function append($what) {
+        if (is_array($what))
+            return $this->extend($what);
+
+        $this->storage[] = $what;
+    }
+
+    function add($what) {
+        $this->append($what);
+    }
+
+    function extend($iterable) {
+        foreach ($iterable as $v)
+            $this->storage[] = $v;
+    }
+
+    function insert($i, $value) {
+        array_splice($this->storage, $i, 0, array($value));
+    }
+
+    function remove($value) {
+        if (!($k = $this->index($value)))
+            throw new OutOfRangeException('No such item in the list');
+        unset($this->storage[$k]);
+    }
+
+    function pop($at=false) {
+        if ($at === false)
+            return array_pop($this->storage);
+        elseif (!isset($this->storage[$at]))
+            throw new OutOfRangeException('Index out of range');
+        else {
+            $rv = array_splice($this->storage, $at, 1);
+            return $rv[0];
+        }
+    }
+
+    function slice($offset, $length=null) {
+        return array_slice($this->storage, $offset, $length);
+    }
+
+    function splice($offset, $length=0, $replacement=null) {
+        return array_splice($this->storage, $offset, $length, $replacement);
+    }
+
+    function index($value) {
+        return array_search($this->storage, $value);
+    }
+
+    /**
+     * Sort the list in place.
+     *
+     * Parameters:
+     * $key - (callable|int) A callable function to produce the sort keys
+     *      or one of the SORT_ constants used by the array_multisort
+     *      function
+     * $reverse - (bool) true if the list should be sorted descending
+     */
+    function sort($key=false, $reverse=false) {
+        if (is_callable($key)) {
+            $keys = array_map($key, $this->storage);
+            array_multisort($keys, $this->storage,
+                $reverse ? SORT_DESC : SORT_ASC);
+        }
+        elseif ($key) {
+            array_multisort($this->storage,
+                $reverse ? SORT_DESC : SORT_ASC, $key);
+        }
+        elseif ($reverse) {
+            rsort($this->storage);
+        }
+        else
+            sort($this->storage);
+    }
+
+    function reverse() {
+        return array_reverse($this->storage);
+    }
+
+    function filter($callable) {
+        $new = new static();
+        foreach ($this->storage as $i=>$v)
+            if ($callable($v, $i))
+                $new[] = $v;
+        return $new;
+    }
+
+    // IteratorAggregate
+    function getIterator() {
+        return new ArrayIterator($this->storage);
+    }
+
+    // Countable
+    function count($mode=COUNT_NORMAL) {
+        return count($this->storage, $mode);
+    }
+
+    // ArrayAccess
+    function offsetGet($offset) {
+        if (!is_int($offset))
+            throw new InvalidArgumentException('List indices should be integers');
+        elseif ($offset < 0)
+            $offset += count($this->storage);
+        if (!isset($this->storage[$offset]))
+            throw new OutOfBoundsException('List index out of range');
+        return $this->storage[$offset];
+    }
+    function offsetSet($offset, $value) {
+        if ($offset === null)
+            return $this->storage[] = $value;
+        elseif (!is_int($offset))
+            throw new InvalidArgumentException('List indices should be integers');
+        elseif ($offset < 0)
+            $offset += count($this->storage);
+
+        if (!isset($this->storage[$offset]))
+            throw new OutOfBoundsException('List assignment out of range');
+
+        $this->storage[$offset] = $value;
+    }
+    function offsetExists($offset) {
+        if (!is_int($offset))
+            throw new InvalidArgumentException('List indices should be integers');
+        elseif ($offset < 0)
+            $offset += count($this->storage);
+        return isset($this->storage[$offset]);
+    }
+    function offsetUnset($offset) {
+        if (!is_int($offset))
+            throw new InvalidArgumentException('List indices should be integers');
+        elseif ($offset < 0)
+            $offset += count($this->storage);
+        unset($this->storage[$offset]);
+    }
+
+    // Serializable
+    function serialize() {
+        return serialize($this->storage);
+    }
+    function unserialize($what) {
+        $this->storage = unserialize($what);
+    }
+
+    function __toString() {
+        return '['.implode(', ', $this->storage).']';
+    }
+}
diff --git a/include/class.validator.php b/include/class.validator.php
index 127cfb93b485eb1c1801ee727112534c86ee6736..c14f910743f85b72be0ab65b97af64c48354ffb3 100644
--- a/include/class.validator.php
+++ b/include/class.validator.php
@@ -13,6 +13,7 @@
 
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
+
 class Validator {
 
     var $input=array();
@@ -34,13 +35,12 @@ class Validator {
 
 
     function validate($source,$userinput=true){
-
         $this->errors=array();
         //Check the input and make sure the fields are specified.
         if(!$source || !is_array($source))
-            $this->errors['err']='Invalid input';
+            $this->errors['err']=__('Invalid input');
         elseif(!$this->fields || !is_array($this->fields))
-            $this->errors['err']='No fields set up';
+            $this->errors['err']=__('No fields set up');
         //Abort on error
         if($this->errors)
             return false;
@@ -112,7 +112,7 @@ class Validator {
                 break;
             case 'password':
                 if(strlen($this->input[$k])<5)
-                    $this->errors[$k]=$field['error'].' (5 chars min)';
+                    $this->errors[$k]=$field['error'].' '.__('(Five characters min)');
                 break;
             case 'username':
                 $error = '';
@@ -124,7 +124,7 @@ class Validator {
                     $this->errors[$k]=$field['error'];
                 break;
             default://If param type is not set...or handle..error out...
-                $this->errors[$k]=$field['error'].' (type not set)';
+                $this->errors[$k]=$field['error'].' '.__('(type not set)');
             endswitch;
         }
         return ($this->errors)?(FALSE):(TRUE);
@@ -191,9 +191,9 @@ class Validator {
 
     function is_username($username, &$error='') {
         if (strlen($username)<2)
-            $error = 'At least two (2) characters';
+            $error = __('Username must have at least two (2) characters');
         elseif (!preg_match('/^[\p{L}\d._-]+$/u', $username))
-            $error = 'Username contains invalid characters';
+            $error = __('Username contains invalid characters');
         return $error == '';
     }
 
diff --git a/include/class.variable.php b/include/class.variable.php
index 5d92709d979666f7435de2733ead2e8e1184d559..41bcb619e5587ddce96dce8bdb16d1e5aec7994b 100644
--- a/include/class.variable.php
+++ b/include/class.variable.php
@@ -116,7 +116,7 @@ class VariableReplacer {
             return $this->variables[$parts[0]];
 
         //Unknown object or variable - leavig it alone.
-        $this->setError('Unknown obj for "'.$var.'" tag ');
+        $this->setError(sprintf(__('Unknown object for "%s" tag'), $var));
         return false;
     }
 
diff --git a/include/class.xml.php b/include/class.xml.php
index ff011eaa4f4a714e2a1f50064dfcb6f5d21cc3d0..129e05877609686f298154bd554cad38d6c55b5a 100644
--- a/include/class.xml.php
+++ b/include/class.xml.php
@@ -39,7 +39,7 @@ class XmlDataParser {
     }
 
     function lastError() {
-        return sprintf("XML error: %s at line %d:%d",
+        return sprintf(__('XML error: %1$s at line %2$d:%3$d'),
             xml_error_string(xml_get_error_code($this->parser)),
             xml_get_current_line_number($this->parser),
             xml_get_current_column_number($this->parser));
diff --git a/include/client/accesslink.inc.php b/include/client/accesslink.inc.php
index f40eb7efc236f2cb05fb091913144ca985385787..9143186b8ed6ea92ce42790ea84ac437a0ebcbb8 100644
--- a/include/client/accesslink.inc.php
+++ b/include/client/accesslink.inc.php
@@ -5,46 +5,52 @@ $email=Format::input($_POST['lemail']?$_POST['lemail']:$_GET['e']);
 $ticketid=Format::input($_POST['lticket']?$_POST['lticket']:$_GET['t']);
 
 if ($cfg->isClientEmailVerificationRequired())
-    $button = "Email Access Link";
+    $button = __("Email Access Link");
 else
-    $button = "View Ticket";
+    $button = __("View Ticket");
 ?>
-<h1>Check Ticket Status</h1>
-<p>Please provide us with your email address and a ticket number, and an access
-link will be emailed to you.</p>
+<h1><?php echo __('Check Ticket Status'); ?></h1>
+<p><?php
+echo __('Please provide your email address and a ticket number.');
+if ($cfg->isClientEmailVerificationRequired())
+    echo ' '.__('An access link will be emailed to you.');
+else
+    echo ' '.__('This will sign you in to view your ticket.');
+?></p>
 <form action="login.php" method="post" id="clientLogin">
     <?php csrf_token(); ?>
 <div style="display:table-row">
-    <div style="width:40%;display:table-cell;box-shadow: 12px 0 15px -15px rgba(0,0,0,0.4);padding-right: 2em;">
-    <strong><?php echo Format::htmlchars($errors['login']); ?></strong>
-    <br>
+    <div class="login-box">
+    <div><strong><?php echo Format::htmlchars($errors['login']); ?></strong></div>
     <div>
-        <label for="email">E-Mail Address:
-        <input id="email" placeholder="e.g. john.doe@osticket.com" type="text"
+        <label for="email"><?php echo __('E-Mail Address'); ?>:
+        <input id="email" placeholder="<?php echo __('e.g. john.doe@osticket.com'); ?>" type="text"
             name="lemail" size="30" value="<?php echo $email; ?>"></label>
     </div>
     <div>
-        <label for="ticketno">Ticket Number:</label><br/>
-        <input id="ticketno" type="text" name="lticket" placeholder="e.g. 051243"
-            size="30" value="<?php echo $ticketid; ?>"></td>
+        <label for="ticketno"><?php echo __('Ticket Number'); ?>:
+        <input id="ticketno" type="text" name="lticket" placeholder="<?php echo __('e.g. 051243'); ?>"
+            size="30" value="<?php echo $ticketid; ?>"></label>
     </div>
     <p>
         <input class="btn" type="submit" value="<?php echo $button; ?>">
     </p>
     </div>
-    <div style="display:table-cell;padding-left: 2em;padding-right:90px;">
+    <div class="instructions">
 <?php if ($cfg && $cfg->getClientRegistrationMode() !== 'disabled') { ?>
-        Have an account with us?
-        <a href="login.php">Sign In</a> <?php
+        <?php echo __('Have an account with us?'); ?>
+        <a href="login.php"><?php echo __('Sign In'); ?></a> <?php
     if ($cfg->isClientRegistrationEnabled()) { ?>
-        or <a href="account.php?do=create">register for an account</a> <?php
-    } ?> to access all your tickets.
-<?php
-} ?>
+<?php echo sprintf(__('or %s register for an account %s to access all your tickets.'),
+    '<a href="account.php?do=create">','</a>');
+    }
+}?>
     </div>
 </div>
 </form>
 <br>
 <p>
-If this is your first time contacting us or you've lost the ticket number, please <a href="open.php">open a new ticket</a>.
+<?php echo sprintf(
+__("If this is your first time contacting us or you've lost the ticket number, please %s open a new ticket %s"),
+    '<a href="open.php">','</a>'); ?>
 </p>
diff --git a/include/client/edit.inc.php b/include/client/edit.inc.php
index e01b2df4938ecde09b38b2db5747cb54d78d85f4..6f364db394f3acaff7c52557764108802578b63e 100644
--- a/include/client/edit.inc.php
+++ b/include/client/edit.inc.php
@@ -5,7 +5,7 @@ if(!defined('OSTCLIENTINC') || !$thisclient || !$ticket || !$ticket->checkUserAc
 ?>
 
 <h1>
-    Editing Ticket #<?php echo $ticket->getNumber(); ?>
+    <?php echo sprintf(__('Editing Ticket #%s'), $ticket->getNumber()); ?>
 </h1>
 
 <form action="tickets.php" method="post">
diff --git a/include/client/faq-category.inc.php b/include/client/faq-category.inc.php
index 11c558615dcd8166716bd3328babd15f5efdb227..5a17e2176b75349d231bf22a00f50c02f39d88c3 100644
--- a/include/client/faq-category.inc.php
+++ b/include/client/faq-category.inc.php
@@ -16,7 +16,7 @@ $sql='SELECT faq.faq_id, question, count(attach.file_id) as attachments '
     .' ORDER BY question';
 if(($res=db_query($sql)) && db_num_rows($res)) {
     echo '
-         <h2>Frequently Asked Questions</h2>
+         <h2>'.__('Frequently Asked Questions').'</h2>
          <div id="faq">
             <ol>';
     while($row=db_fetch_array($res)) {
@@ -27,8 +27,8 @@ if(($res=db_query($sql)) && db_num_rows($res)) {
     }
     echo '  </ol>
          </div>
-         <p><a class="back" href="index.php">&laquo; Go Back</a></p>';
+         <p><a class="back" href="index.php">&laquo; '.__('Go Back').'</a></p>';
 }else {
-    echo '<strong>Category does not have any FAQs. <a href="index.php">Back To Index</a></strong>';
+    echo '<strong>'.__('This category does not have any FAQs.').' <a href="index.php">'.__('Back To Index').'</a></strong>';
 }
 ?>
diff --git a/include/client/faq.inc.php b/include/client/faq.inc.php
index 9dc70af789f3fab716613795b46278042d5351bf..2ccc475e0349e3e83e5a80eab71a0c9eba9ab156 100644
--- a/include/client/faq.inc.php
+++ b/include/client/faq.inc.php
@@ -4,15 +4,15 @@ if(!defined('OSTCLIENTINC') || !$faq  || !$faq->isPublished()) die('Access Denie
 $category=$faq->getCategory();
 
 ?>
-<h1>Frequently Asked Questions</h1>
+<h1><?php echo __('Frequently Asked Questions');?></h1>
 <div id="breadcrumbs">
-    <a href="index.php">All Categories</a>
+    <a href="index.php"><?php echo __('All Categories');?></a>
     &raquo; <a href="faq.php?cid=<?php echo $category->getId(); ?>"><?php echo $category->getName(); ?></a>
 </div>
-<div style="width:700px;padding-top:2px; float:left;">
+<div style="width:700px;padding-top:2px;" class="pull-left">
 <strong style="font-size:16px;"><?php echo $faq->getQuestion() ?></strong>
 </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;"></div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;"></div>
 <div class="clear"></div>
 <p>
 <?php echo Format::safe_html($faq->getAnswerWithImages()); ?>
@@ -20,13 +20,13 @@ $category=$faq->getCategory();
 <p>
 <?php
 if($faq->getNumAttachments()) { ?>
- <div><span class="faded"><b>Attachments:</b></span>  <?php echo $faq->getAttachmentsLinks(); ?></div>
+ <div><span class="faded"><b><?php echo __('Attachments');?>:</b></span>  <?php echo $faq->getAttachmentsLinks(); ?></div>
 <?php
 } ?>
 
-<div class="article-meta"><span class="faded"><b>Help Topics:</b></span>
+<div class="article-meta"><span class="faded"><b><?php echo __('Help Topics');?>:</b></span>
     <?php echo ($topics=$faq->getHelpTopics())?implode(', ',$topics):' '; ?>
 </div>
 </p>
 <hr>
-<div class="faded">&nbsp;Last updated <?php echo Format::db_daydatetime($category->getUpdateDate()); ?></div>
+<div class="faded">&nbsp;<?php echo __('Last updated').' '.Format::db_daydatetime($category->getUpdateDate()); ?></div>
diff --git a/include/client/footer.inc.php b/include/client/footer.inc.php
index bce8b7469c6192673e6a9bafcd9d73f020078eb5..9ff4ad15265c68a7a7938535786cae3ea9f910db 100644
--- a/include/client/footer.inc.php
+++ b/include/client/footer.inc.php
@@ -2,12 +2,17 @@
     </div>
     <div id="footer">
         <p>Copyright &copy; <?php echo date('Y'); ?> <?php echo (string) $ost->company ?: 'osTicket.com'; ?> - All rights reserved.</p>
-        <a id="poweredBy" href="http://osticket.com" target="_blank">Helpdesk software - powered by osTicket</a>
+        <a id="poweredBy" href="http://osticket.com" target="_blank"><?php echo __('Helpdesk software - powered by osTicket'); ?></a>
     </div>
 <div id="overlay"></div>
 <div id="loading">
-    <h4>Please Wait!</h4>
-    <p>Please wait... it will take a second!</p>
+    <h4><?php echo __('Please Wait!');?></h4>
+    <p><?php echo __('Please wait... it will take a second!');?></p>
 </div>
+<?php
+if (($lang = Internationalization::getCurrentLanguage()) && $lang != 'en_US') { ?>
+    <script type="text/javascript" src="ajax.php/i18n/<?php
+        echo $lang; ?>/js"></script>
+<?php } ?>
 </body>
 </html>
diff --git a/include/client/header.inc.php b/include/client/header.inc.php
index 9fdea9c36540a415d598be51b2d19a8f379d7f47..92e9dfe13bdda7cc9ad08fc6cedb9b6f7c0d0b7b 100644
--- a/include/client/header.inc.php
+++ b/include/client/header.inc.php
@@ -1,5 +1,6 @@
 <?php
-$title=($cfg && is_object($cfg) && $cfg->getTitle())?$cfg->getTitle():'osTicket :: Support Ticket System';
+$title=($cfg && is_object($cfg) && $cfg->getTitle())
+    ? $cfg->getTitle() : 'osTicket :: '.__('Support Ticket System');
 $signin_url = ROOT_PATH . "login.php"
     . ($thisclient ? "?e=".urlencode($thisclient->getEmail()) : "");
 $signout_url = ROOT_PATH . "logout.php?auth=".$ost->getLinkToken();
@@ -7,7 +8,12 @@ $signout_url = ROOT_PATH . "logout.php?auth=".$ost->getLinkToken();
 header("Content-Type: text/html; charset=UTF-8\r\n");
 ?>
 <!DOCTYPE html>
-<html>
+<html <?php
+if (($lang = Internationalization::getCurrentLanguage())
+        && ($info = Internationalization::getLanguageInfo($lang))
+        && (@$info['direction'] == 'rtl'))
+    echo 'dir="rtl" class="rtl"';
+?>>
 <head>
     <meta charset="utf-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
@@ -15,7 +21,7 @@ header("Content-Type: text/html; charset=UTF-8\r\n");
     <meta name="description" content="customer support platform">
     <meta name="keywords" content="osTicket, Customer support system, support ticket system">
     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
-    <link rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/osticket.css" media="screen">
+	<link rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/osticket.css" media="screen">
     <link rel="stylesheet" href="<?php echo ASSETS_PATH; ?>css/theme.css" media="screen">
     <link rel="stylesheet" href="<?php echo ASSETS_PATH; ?>css/print.css" media="print">
     <link rel="stylesheet" href="<?php echo ROOT_PATH; ?>scp/css/typeahead.css"
@@ -25,13 +31,17 @@ header("Content-Type: text/html; charset=UTF-8\r\n");
     <link rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/thread.css" media="screen">
     <link rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/redactor.css" media="screen">
     <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/font-awesome.min.css">
+    <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/flags.css">
+    <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/rtl.css"/>
     <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jquery-1.8.3.min.js"></script>
     <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jquery-ui-1.10.3.custom.min.js"></script>
-    <script src="<?php echo ROOT_PATH; ?>js/jquery.multifile.js"></script>
     <script src="<?php echo ROOT_PATH; ?>js/osticket.js"></script>
+    <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/filedrop.field.js"></script>
+    <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jquery.multiselect.min.js"></script>
     <script src="<?php echo ROOT_PATH; ?>scp/js/bootstrap-typeahead.js"></script>
     <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor.min.js"></script>
     <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor-osticket.js"></script>
+    <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor-fonts.js"></script>
     <?php
     if($ost && ($headers=$ost->getExtraHeaders())) {
         echo "\n\t".implode("\n\t", $headers)."\n";
@@ -41,37 +51,55 @@ header("Content-Type: text/html; charset=UTF-8\r\n");
 <body>
     <div id="container">
         <div id="header">
-            <a id="logo" href="<?php echo ROOT_PATH; ?>index.php"
-            title="Support Center"><img src="<?php echo ROOT_PATH; ?>logo.php" border=0 alt="<?php
+            <a class="pull-left" id="logo" href="<?php echo ROOT_PATH; ?>index.php"
+            title="<?php echo __('Support Center'); ?>"><img src="<?php
+                echo ROOT_PATH; ?>logo.php" border=0 alt="<?php
                 echo $ost->getConfig()->getTitle(); ?>"
                 style="height: 5em"></a>
+            <div class="pull-right flush-right">
             <p>
              <?php
                 if ($thisclient && is_object($thisclient) && $thisclient->isValid()
                     && !$thisclient->isGuest()) {
                  echo Format::htmlchars($thisclient->getName()).'&nbsp;|';
                  ?>
-                <a href="<?php echo ROOT_PATH; ?>profile.php">Profile</a> |
-                <a href="<?php echo ROOT_PATH; ?>tickets.php">Tickets <b>(<?php echo $thisclient->getNumTickets(); ?>)</b></a> -
-                <a href="<?php echo $signout_url; ?>">Sign Out</a>
+                <a href="<?php echo ROOT_PATH; ?>profile.php"><?php echo __('Profile'); ?></a> |
+                <a href="<?php echo ROOT_PATH; ?>tickets.php"><?php echo sprintf(__('Tickets <b>(%d)</b>'), $thisclient->getNumTickets()); ?></a> -
+                <a href="<?php echo $signout_url; ?>"><?php echo __('Sign Out'); ?></a>
             <?php
             } elseif($nav) {
                 if ($cfg->getClientRegistrationMode() == 'public') { ?>
-                    Guest User | <?php
+                    <?php echo __('Guest User'); ?> | <?php
                 }
                 if ($thisclient && $thisclient->isValid() && $thisclient->isGuest()) { ?>
-                    <a href="<?php echo $signout_url; ?>">Sign Out</a><?php
+                    <a href="<?php echo $signout_url; ?>"><?php echo __('Sign Out'); ?></a><?php
                 }
                 elseif ($cfg->getClientRegistrationMode() != 'disabled') { ?>
-                    <a href="<?php echo $signin_url; ?>">Sign In</a>
+                    <a href="<?php echo $signin_url; ?>"><?php echo __('Sign In'); ?></a>
 <?php
                 }
             } ?>
             </p>
+            <p>
+<?php
+if (($all_langs = Internationalization::availableLanguages())
+    && (count($all_langs) > 1)
+) {
+    foreach ($all_langs as $code=>$info) {
+        list($lang, $locale) = explode('_', $code);
+?>
+        <a class="flag flag-<?php echo strtolower($locale ?: $info['flag'] ?: $lang); ?>"
+            href="?<?php echo urlencode($_GET['QUERY_STRING']); ?>&amp;lang=<?php echo $code;
+            ?>" title="<?php echo Internationalization::getLanguageDescription($code); ?>">&nbsp;</a>
+<?php }
+} ?>
+            </p>
+            </div>
         </div>
+        <div class="clear"></div>
         <?php
         if($nav){ ?>
-        <ul id="nav">
+        <ul id="nav" class="flush-left">
             <?php
             if($nav && ($navs=$nav->getNavLinks()) && is_array($navs)){
                 foreach($navs as $name =>$nav) {
diff --git a/include/client/knowledgebase.inc.php b/include/client/knowledgebase.inc.php
index 79bfa6836857a8b3123d030d8dbfd34c34e2da8a..c3ab59ef5098f48963ae85212d14ee1ad9b5a60a 100644
--- a/include/client/knowledgebase.inc.php
+++ b/include/client/knowledgebase.inc.php
@@ -2,13 +2,13 @@
 if(!defined('OSTCLIENTINC')) die('Access Denied');
 
 ?>
-<h1>Frequently Asked Questions</h1>
+<h1><?php echo __('Frequently Asked Questions');?></h1>
 <form action="index.php" method="get" id="kb-search">
     <input type="hidden" name="a" value="search">
     <div>
         <input id="query" type="text" size="20" name="q" value="<?php echo Format::htmlchars($_REQUEST['q']); ?>">
         <select name="cid" id="cid">
-            <option value="">&mdash; All Categories &mdash;</option>
+            <option value="">&mdash; <?php echo __('All Categories');?> &mdash;</option>
             <?php
             $sql='SELECT category_id, name, count(faq.category_id) as faqs '
                 .' FROM '.FAQ_CATEGORY_TABLE.' cat '
@@ -27,11 +27,11 @@ if(!defined('OSTCLIENTINC')) die('Access Denied');
             }
             ?>
         </select>
-        <input id="searchSubmit" type="submit" value="Search">
+        <input id="searchSubmit" type="submit" value="<?php echo __('Search');?>">
     </div>
     <div>
         <select name="topicId" id="topic-id">
-            <option value="">&mdash; All Help Topics &mdash;</option>
+            <option value="">&mdash; <?php echo __('All Help Topics');?> &mdash;</option>
             <?php
             $sql='SELECT ht.topic_id, CONCAT_WS(" / ", pht.topic, ht.topic) as helptopic, count(faq.topic_id) as faqs '
                 .' FROM '.TOPIC_TABLE.' ht '
@@ -79,19 +79,19 @@ if($_REQUEST['q'] || $_REQUEST['cid'] || $_REQUEST['topicId']) { //Search.
     }
 
     $sql.=' GROUP BY faq.faq_id ORDER BY question';
-    echo "<div><strong>Search Results</strong></div><div class='clear'></div>";
+    echo "<div><strong>".__('Search Results').'</strong></div><div class="clear"></div>';
     if(($res=db_query($sql)) && ($num=db_num_rows($res))) {
-        echo '<div id="faq">'.$num.' FAQs matched your search criteria.
+        echo '<div id="faq">'.sprintf(__('%d FAQs matched your search criteria.'),$num).'
                 <ol>';
         while($row=db_fetch_array($res)) {
             echo sprintf('
                 <li><a href="faq.php?id=%d" class="previewfaq">%s</a></li>',
-                $row['faq_id'],$row['question'],$row['ispublished']?'Published':'Internal');
+                $row['faq_id'],$row['question'],$row['ispublished']?__('Published'):__('Internal'));
         }
         echo '  </ol>
              </div>';
     } else {
-        echo '<strong class="faded">The search did not match any FAQs.</strong>';
+        echo '<strong class="faded">'.__('The search did not match any FAQs.').'</strong>';
     }
 } else { //Category Listing.
     $sql='SELECT cat.category_id, cat.name, cat.description, cat.ispublic, count(faq.faq_id) as faqs '
@@ -102,7 +102,7 @@ if($_REQUEST['q'] || $_REQUEST['cid'] || $_REQUEST['topicId']) { //Search.
         .' HAVING faqs>0 '
         .' ORDER BY cat.name';
     if(($res=db_query($sql)) && db_num_rows($res)) {
-        echo '<div>Click on the category to browse FAQs.</div>
+        echo '<div>'.__('Click on the category to browse FAQs.').'</div>
                 <ul id="kb">';
         while($row=db_fetch_array($res)) {
 
@@ -117,7 +117,7 @@ if($_REQUEST['q'] || $_REQUEST['cid'] || $_REQUEST['topicId']) { //Search.
         }
         echo '</ul>';
     } else {
-        echo 'NO FAQs found';
+        echo __('NO FAQs found');
     }
 }
 ?>
diff --git a/include/client/login.inc.php b/include/client/login.inc.php
index 7d5f9e36d905a13db5d6cace376484bcce63e5e1..2eb28a227d004e2cd9e9f9e357b0b8c064811c78 100644
--- a/include/client/login.inc.php
+++ b/include/client/login.inc.php
@@ -10,9 +10,8 @@ if ($content) {
     list($title, $body) = $ost->replaceTemplateVariables(
         array($content->getName(), $content->getBody()));
 } else {
-    $title = 'Sign In';
-    $body = 'To better serve you, we encourage our clients to register for
-        an account and verify the email address we have on record.';
+    $title = __('Sign In');
+    $body = __('To better serve you, we encourage our clients to register for an account and verify the email address we have on record.');
 }
 
 ?>
@@ -21,18 +20,18 @@ if ($content) {
 <form action="login.php" method="post" id="clientLogin">
     <?php csrf_token(); ?>
 <div style="display:table-row">
-    <div style="width:40%;display:table-cell;box-shadow: 12px 0 15px -15px rgba(0,0,0,0.4);padding:15px;">
+    <div class="login-box">
     <strong><?php echo Format::htmlchars($errors['login']); ?></strong>
     <div>
-        <input id="username" placeholder="Email or Username" type="text" name="luser" size="30" value="<?php echo $email; ?>">
+        <input id="username" placeholder="<?php echo __('Email or Username'); ?>" type="text" name="luser" size="30" value="<?php echo $email; ?>">
     </div>
     <div>
-        <input id="passwd" placeholder="Password" type="password" name="lpasswd" size="30" value="<?php echo $passwd; ?>"></td>
+        <input id="passwd" placeholder="<?php echo __('Password'); ?>" type="password" name="lpasswd" size="30" value="<?php echo $passwd; ?>"></td>
     </div>
     <p>
-        <input class="btn" type="submit" value="Sign In">
+        <input class="btn" type="submit" value="<?php echo __('Sign In'); ?>">
 <?php if ($suggest_pwreset) { ?>
-        <a style="padding-top:4px;display:inline-block;" href="pwreset.php">Forgot My Password</a>
+        <a style="padding-top:4px;display:inline-block;" href="pwreset.php"><?php echo __('Forgot My Password'); ?></a>
 <?php } ?>
     </p>
     </div>
@@ -52,19 +51,20 @@ if (count($ext_bks)) {
 if ($cfg && $cfg->isClientRegistrationEnabled()) {
     if (count($ext_bks)) echo '<hr style="width:70%"/>'; ?>
     <div style="margin-bottom: 5px">
-    Not yet registered? <a href="account.php?do=create">Create an account</a>
+    <?php echo __('Not yet registered?'); ?> <a href="account.php?do=create"><?php echo __('Create an account'); ?></a>
     </div>
 <?php } ?>
     <div>
-    <b>I'm an agent</b> —
-    <a href="<?php echo ROOT_PATH; ?>scp">sign in here</a>
+    <b><?php echo __("I'm an agent"); ?></b> —
+    <a href="<?php echo ROOT_PATH; ?>scp"><?php echo __('sign in here'); ?></a>
     </div>
     </div>
 </div>
 </form>
 <br>
 <p>
-<?php if ($cfg && !$cfg->isClientLoginRequired()) { ?>
-If this is your first time contacting us or you've lost the ticket number, please <a href="open.php">open a new ticket</a>.
-<?php } ?>
+<?php if ($cfg && !$cfg->isClientLoginRequired()) {
+    echo sprintf(__('If this is your first time contacting us or you\'ve lost the ticket number, please %s open a new ticket %s'),
+        '<a href="open.php">', '</a>');
+} ?>
 </p>
diff --git a/include/client/open.inc.php b/include/client/open.inc.php
index 9cb1bb65739a4c3504ef701ae128378c943f91b0..821aa6ef9cc8661e5126b070dc5d35fedc09d1df 100644
--- a/include/client/open.inc.php
+++ b/include/client/open.inc.php
@@ -22,22 +22,29 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
 }
 
 ?>
-<h1>Open a New Ticket</h1>
-<p>Please fill in the form below to open a new ticket.</p>
+<h1><?php echo __('Open a New Ticket');?></h1>
+<p><?php echo __('Please fill in the form below to open a new ticket.');?></p>
 <form id="ticketForm" method="post" action="open.php" enctype="multipart/form-data">
   <?php csrf_token(); ?>
   <input type="hidden" name="a" value="open">
   <table width="800" cellpadding="1" cellspacing="0" border="0">
     <tbody>
     <tr>
-        <td class="required">Help Topic:</td>
+        <td class="required"><?php echo __('Help Topic');?>:</td>
         <td>
             <select id="topicId" name="topicId" onchange="javascript:
                     var data = $(':input[name]', '#dynamic-form').serialize();
-                    $('#dynamic-form').load(
-                        'ajax.php/form/help-topic/' + this.value, data);
-                    ">
-                <option value="" selected="selected">&mdash; Select a Help Topic &mdash;</option>
+                    $.ajax(
+                      'ajax.php/form/help-topic/' + this.value,
+                      {
+                        data: data,
+                        dataType: 'json',
+                        success: function(json) {
+                          $('#dynamic-form').empty().append(json.html);
+                          $(document.head).append(json.media);
+                        }
+                      });">
+                <option value="" selected="selected">&mdash; <?php echo __('Select a Help Topic');?> &mdash;</option>
                 <?php
                 if($topics=Topic::getPublicHelpTopics()) {
                     foreach($topics as $id =>$name) {
@@ -45,7 +52,7 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
                                 $id, ($info['topicId']==$id)?'selected="selected"':'', $name);
                     }
                 } else { ?>
-                    <option value="0" >General Inquiry</option>
+                    <option value="0" ><?php echo __('General Inquiry');?></option>
                 <?php
                 } ?>
             </select>
@@ -60,8 +67,8 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
         }
         else { ?>
             <tr><td colspan="2"><hr /></td></tr>
-        <tr><td>Email:</td><td><?php echo $thisclient->getEmail(); ?></td></tr>
-        <tr><td>Client:</td><td><?php echo $thisclient->getName(); ?></td></tr>
+        <tr><td><?php echo __('Email'); ?>:</td><td><?php echo $thisclient->getEmail(); ?></td></tr>
+        <tr><td><?php echo __('Client'); ?>:</td><td><?php echo $thisclient->getName(); ?></td></tr>
         <?php } ?>
     </tbody>
     <tbody id="dynamic-form">
@@ -78,15 +85,15 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
     <?php
     if($cfg && $cfg->isCaptchaEnabled() && (!$thisclient || !$thisclient->isValid())) {
         if($_POST && $errors && !$errors['captcha'])
-            $errors['captcha']='Please re-enter the text again';
+            $errors['captcha']=__('Please re-enter the text again');
         ?>
     <tr class="captchaRow">
-        <td class="required">CAPTCHA Text:</td>
+        <td class="required"><?php echo __('CAPTCHA Text');?>:</td>
         <td>
             <span class="captcha"><img src="captcha.php" border="0" align="left"></span>
             &nbsp;&nbsp;
             <input id="captcha" type="text" name="captcha" size="6" autocomplete="off">
-            <em>Enter the text shown on the image.</em>
+            <em><?php echo __('Enter the text shown on the image.');?></em>
             <font class="error">*&nbsp;<?php echo $errors['captcha']; ?></font>
         </td>
     </tr>
@@ -97,9 +104,9 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
   </table>
 <hr/>
   <p style="text-align:center;">
-        <input type="submit" value="Create Ticket">
-        <input type="reset" name="reset" value="Reset">
-        <input type="button" name="cancel" value="Cancel" onclick="javascript:
+        <input type="submit" value="<?php echo __('Create Ticket');?>">
+        <input type="reset" name="reset" value="<?php echo __('Reset');?>">
+        <input type="button" name="cancel" value="<?php echo __('Cancel'); ?>" onclick="javascript:
             $('.richtext').each(function() {
                 var redactor = $(this).data('redactor');
                 if (redactor && redactor.opts.draftDelete)
diff --git a/include/client/profile.inc.php b/include/client/profile.inc.php
index 8586c3976f1a117c4887b44670e905c1ee073b8f..89fa70a095d56c8f68c54d2ae9beb0d23648e299 100644
--- a/include/client/profile.inc.php
+++ b/include/client/profile.inc.php
@@ -1,10 +1,7 @@
-<?php
-
-?>
-<h1>Manage Your Profile Information</h1>
-<p>
-Use the forms below to update the information we have on file for your
-account
+<h1><?php echo __('Manage Your Profile Information'); ?></h1>
+<p><?php echo __(
+'Use the forms below to update the information we have on file for your account'
+); ?>
 </p>
 <form action="profile.php" method="post">
   <?php csrf_token(); ?>
@@ -19,14 +16,14 @@ if ($acct = $thisclient->getAccount()) {
 ?>
 <tr>
     <td colspan="2">
-        <div><hr><h3>Preferences</h3>
+        <div><hr><h3><?php echo __('Preferences'); ?></h3>
         </div>
     </td>
 </tr>
-    <td>Time Zone:</td>
+    <td><?php echo __('Time Zone'); ?>:</td>
     <td>
         <select name="timezone_id" id="timezone_id">
-            <option value="0">&mdash; Select Time Zone &mdash;</option>
+            <option value="0">&mdash; <?php echo __('Select Time Zone'); ?> &mdash;</option>
             <?php
             $sql='SELECT id, offset,timezone FROM '.TIMEZONE_TABLE.' ORDER BY id';
             if(($res=db_query($sql)) && db_num_rows($res)){
@@ -42,24 +39,43 @@ if ($acct = $thisclient->getAccount()) {
 </tr>
 <tr>
     <td width="180">
-       Daylight Saving:
+        <?php echo __('Daylight Saving') ?>:
     </td>
     <td>
         <input type="checkbox" name="dst" value="1" <?php echo $info['dst']?'checked="checked"':''; ?>>
-        Observe daylight saving
-        <em>(Current Time: <strong><?php echo Format::date($cfg->getDateTimeFormat(),Misc::gmtime(),$info['tz_offset'],$info['dst']); ?></strong>)</em>
+        <?php echo __('Observe daylight saving'); ?>
+        <em>(<?php __('Current Time'); ?>:
+            <strong><?php echo Format::date($cfg->getDateTimeFormat(),Misc::gmtime(),$info['tz_offset'],$info['dst']); ?></strong>)</em>
     </td>
 </tr>
+    <tr>
+        <td width="180">
+            <?php echo __('Preferred Language'); ?>:
+        </td>
+        <td>
+    <?php
+    $langs = Internationalization::availableLanguages(); ?>
+            <select name="lang">
+                <option value="">&mdash; <?php echo __('Use Browser Preference'); ?> &mdash;</option>
+<?php foreach($langs as $l) {
+$selected = ($info['lang'] == $l['code']) ? 'selected="selected"' : ''; ?>
+                <option value="<?php echo $l['code']; ?>" <?php echo $selected;
+                    ?>><?php echo Internationalization::getLanguageDescription($l['code']); ?></option>
+<?php } ?>
+            </select>
+            <span class="error">&nbsp;<?php echo $errors['lang']; ?></span>
+        </td>
+    </tr>
 <?php if ($acct->isPasswdResetEnabled()) { ?>
 <tr>
     <td colspan=2">
-        <div><hr><h3>Access Credentials</h3></div>
+        <div><hr><h3><?php echo __('Access Credentials'); ?></h3></div>
     </td>
 </tr>
 <?php if (!isset($_SESSION['_client']['reset-token'])) { ?>
 <tr>
     <td width="180">
-        Current Password:
+        <?php echo __('Current Password'); ?>:
     </td>
     <td>
         <input type="password" size="18" name="cpasswd" value="<?php echo $info['cpasswd']; ?>">
@@ -69,7 +85,7 @@ if ($acct = $thisclient->getAccount()) {
 <?php } ?>
 <tr>
     <td width="180">
-        New Password:
+        <?php echo __('New Password'); ?>:
     </td>
     <td>
         <input type="password" size="18" name="passwd1" value="<?php echo $info['passwd1']; ?>">
@@ -78,7 +94,7 @@ if ($acct = $thisclient->getAccount()) {
 </tr>
 <tr>
     <td width="180">
-        Confirm New Password:
+        <?php echo __('Confirm New Password'); ?>:
     </td>
     <td>
         <input type="password" size="18" name="passwd2" value="<?php echo $info['passwd2']; ?>">
diff --git a/include/client/pwreset.login.php b/include/client/pwreset.login.php
index c2bc7e39935259b38c560bf1ccd9ee34a05c6ee1..f30be3d59472fece4c1276a99275240b47d52090 100644
--- a/include/client/pwreset.login.php
+++ b/include/client/pwreset.login.php
@@ -3,11 +3,10 @@ if(!defined('OSTCLIENTINC')) die('Access Denied');
 
 $userid=Format::input($_POST['userid']);
 ?>
-<h1>Forgot My Password</h1>
-<p>
-Enter your username or email address again in the form below and press the
-<strong>Login</strong> to access your account and reset your password.
-
+<h1><?php echo __('Forgot My Password'); ?></h1>
+<p><?php echo __(
+'Enter your username or email address again in the form below and press the <strong>Login</strong> to access your account and reset your password.');
+?>
 <form action="pwreset.php" method="post" id="clientLogin">
     <div style="width:50%;display:inline-block">
     <?php csrf_token(); ?>
@@ -16,7 +15,7 @@ Enter your username or email address again in the form below and press the
     <strong><?php echo Format::htmlchars($banner); ?></strong>
     <br>
     <div>
-        <label for="username">Username:</label>
+        <label for="username"><?php echo __('Username'); ?>:</label>
         <input id="username" type="text" name="userid" size="30" value="<?php echo $userid; ?>">
     </div>
     <p>
diff --git a/include/client/pwreset.request.php b/include/client/pwreset.request.php
index 28922c8d2a9d6028ab18ec533ea7be9873c847ec..cfadbb2ffb37c89a2a9f66896b03e85168a9108d 100644
--- a/include/client/pwreset.request.php
+++ b/include/client/pwreset.request.php
@@ -3,11 +3,10 @@ if(!defined('OSTCLIENTINC')) die('Access Denied');
 
 $userid=Format::input($_POST['userid']);
 ?>
-<h1>Forgot My Password</h1>
-<p>
-Enter your username or email address in the form below and press the
-<strong>Send Email</strong> button to have a password reset link sent to
-your email account on file.
+<h1><?php echo __('Forgot My Password'); ?></h1>
+<p><?php echo __(
+'Enter your username or email address in the form below and press the <strong>Send Email</strong> button to have a password reset link sent to your email account on file.');
+?>
 
 <form action="pwreset.php" method="post" id="clientLogin">
     <div style="width:50%;display:inline-block">
@@ -16,11 +15,11 @@ your email account on file.
     <strong><?php echo Format::htmlchars($banner); ?></strong>
     <br>
     <div>
-        <label for="username">Username:</label>
+        <label for="username"><?php echo __('Username'); ?>:</label>
         <input id="username" type="text" name="userid" size="30" value="<?php echo $userid; ?>">
     </div>
     <p>
-        <input class="btn" type="submit" value="Send Email">
+        <input class="btn" type="submit" value="<?php echo __('Send Email'); ?>">
     </p>
     </div>
 </form>
diff --git a/include/client/pwreset.sent.php b/include/client/pwreset.sent.php
index c76f50957ecc56ec8a2946a26d729e364d332968..91c4720bceeda0ed5f4d0229156b3ee04497d8ea 100644
--- a/include/client/pwreset.sent.php
+++ b/include/client/pwreset.sent.php
@@ -1,13 +1,11 @@
-<h1>Forgot My Password</h1>
-<p>
-Enter your username or email address in the form below and press the
-<strong>Send Email</strong> button to have a password reset link sent to
-your email account on file.
+<h1><?php echo __('Forgot My Password'); ?></h1>
+<p><?php echo __(
+'Enter your username or email address in the form below and press the <strong>Send Email</strong> button to have a password reset link sent to your email account on file.');
+?>
 
 <form action="pwreset.php" method="post" id="clientLogin">
-    <div style="width:50%;display:inline-block">
-    We have sent you a reset email to the email address you have on file for
-    your account. If you do not receive the email or cannot reset your
-    password, please ...
+<div style="width:50%;display:inline-block"><?php echo __(
+    'We have sent you a reset email to the email address you have on file for your account. If you do not receive the email or cannot reset your password, please submit a ticket to have your account unlocked.'
+); ?>
     </div>
 </form>
diff --git a/include/client/register.confirm.inc.php b/include/client/register.confirm.inc.php
index a0628c0bcd287ed760cc95d48b44f8b1dae380bd..daa7e9a05311f794e941a496889c14d6f1572574 100644
--- a/include/client/register.confirm.inc.php
+++ b/include/client/register.confirm.inc.php
@@ -6,12 +6,12 @@
 echo Format::display($body); ?>
 </p>
 <?php } else { ?>
-<h1>Account Registration</h1>
+<h1><?php echo __('Account Registration'); ?></h1>
 <p>
-<strong>Thanks for registering for an account.</strong>
+<strong><?php echo __('Thanks for registering for an account.'); ?></strong>
 </p>
-<p>
-We've just sent you an email to the address you entered. Please follow the
-link in the email to confirm your account and gain access to your tickets.
+<p><?php echo __(
+"We've just sent you an email to the address you entered. Please follow the link in the email to confirm your account and gain access to your tickets."
+); ?>
 </p>
 <?php } ?>
diff --git a/include/client/register.confirmed.inc.php b/include/client/register.confirmed.inc.php
index 5bba31e8c539699fa2834be37c03a86a3cca30e6..5c68718c7a1e4c2f6aa019a085644de5c05b9670 100644
--- a/include/client/register.confirmed.inc.php
+++ b/include/client/register.confirmed.inc.php
@@ -6,13 +6,13 @@
 echo Format::display($body); ?>
 </p>
 <?php } else { ?>
-<h1>Account Registration</h1>
+<h1><?php echo __('Account Registration'); ?></h1>
 <p>
-<strong>Thanks for registering for an account.</strong>
+<strong><?php echo __('Thanks for registering for an account.'); ?></strong>
 </p>
-<p>
-You've confirmed your email address and successfully activated your account.
-You may proceed to check on previously opened tickets or open a new ticket.
+<p><?php echo __(
+"You've confirmed your email address and successfully activated your account.  You may proceed to check on previously opened tickets or open a new ticket."
+); ?>
 </p>
-<p><em>Your friendly support center</em></p>
+<p><em><?php echo __('Your friendly support center'); ?></em></p>
 <?php } ?>
diff --git a/include/client/register.inc.php b/include/client/register.inc.php
index 9c43bdf0542d4c442f8960d9b813150b4819c8dc..78b42ac9474fc251c3e937f5989677cbc2a70a3e 100644
--- a/include/client/register.inc.php
+++ b/include/client/register.inc.php
@@ -16,10 +16,10 @@ if (isset($user) && $user instanceof ClientCreateRequest) {
 $info = Format::htmlchars(($errors && $_POST)?$_POST:$info);
 
 ?>
-<h1>Account Registration</h1>
-<p>
-Use the forms below to create or update the information we have on file for
-your account
+<h1><?php echo __('Account Registration'); ?></h1>
+<p><?php echo __(
+'Use the forms below to create or update the information we have on file for your account'
+); ?>
 </p>
 <form action="account.php" method="post">
   <?php csrf_token(); ?>
@@ -33,11 +33,11 @@ your account
 ?>
 <tr>
     <td colspan="2">
-        <div><hr><h3>Preferences</h3>
+        <div><hr><h3><?php echo __('Preferences'); ?></h3>
         </div>
     </td>
 </tr>
-    <td>Time Zone:</td>
+    <td><?php echo __('Time Zone'); ?>:</td>
     <td>
         <select name="timezone_id" id="timezone_id">
             <?php
@@ -55,30 +55,31 @@ your account
 </tr>
 <tr>
     <td width="180">
-       Daylight Saving:
+        <?php echo __('Daylight Saving'); ?>:
     </td>
     <td>
         <input type="checkbox" name="dst" value="1" <?php echo $info['dst']?'checked="checked"':''; ?>>
-        Observe daylight saving
-        <em>(Current Time: <strong><?php echo Format::date($cfg->getDateTimeFormat(),Misc::gmtime(),$info['tz_offset'],$info['dst']); ?></strong>)</em>
+        <?php echo __('Observe daylight saving'); ?>
+        <em>(<?php echo __('Current Time'); ?>:
+            <strong><?php echo Format::date($cfg->getDateTimeFormat(),Misc::gmtime(),$info['tz_offset'],$info['dst']); ?></strong>)</em>
     </td>
 </tr>
 <tr>
     <td colspan=2">
-        <div><hr><h3>Access Credentials</h3></div>
+        <div><hr><h3><?php echo __('Access Credentials'); ?></h3></div>
     </td>
 </tr>
 <?php if ($info['backend']) { ?>
 <tr>
     <td width="180">
-        Login With:
+        <?php echo __('Login With'); ?>:
     </td>
     <td>
         <input type="hidden" name="backend" value="<?php echo $info['backend']; ?>"/>
         <input type="hidden" name="username" value="<?php echo $info['username']; ?>"/>
 <?php foreach (UserAuthenticationBackend::allRegistered() as $bk) {
     if ($bk::$id == $info['backend']) {
-        echo $bk::$name;
+        echo $bk->getName();
         break;
     }
 } ?>
@@ -87,7 +88,7 @@ your account
 <?php } else { ?>
 <tr>
     <td width="180">
-        Create a Password:
+        <?php echo __('Create a Password'); ?>:
     </td>
     <td>
         <input type="password" size="18" name="passwd1" value="<?php echo $info['passwd1']; ?>">
@@ -96,7 +97,7 @@ your account
 </tr>
 <tr>
     <td width="180">
-        Confirm New Password:
+        <?php echo __('Confirm New Password'); ?>:
     </td>
     <td>
         <input type="password" size="18" name="passwd2" value="<?php echo $info['passwd2']; ?>">
diff --git a/include/client/templates/dynamic-form.tmpl.php b/include/client/templates/dynamic-form.tmpl.php
index c8e7090ff7c6de45686ee27aa1095e9c440ec5cc..12b3944075e71ad3788be4be515fadb5478a56e8 100644
--- a/include/client/templates/dynamic-form.tmpl.php
+++ b/include/client/templates/dynamic-form.tmpl.php
@@ -5,6 +5,8 @@
     ?>
     <tr><td colspan="2"><hr />
     <div class="form-header" style="margin-bottom:0.5em">
+    <?php print ($form instanceof DynamicFormEntry) 
+        ? $form->getForm()->getMedia() : $form->getMedia(); ?>
     <h3><?php echo Format::htmlchars($form->getTitle()); ?></h3>
     <em><?php echo Format::htmlchars($form->getInstructions()); ?></em>
     </div>
@@ -14,7 +16,7 @@
     // 'private' are not included in the output for clients
     global $thisclient;
     foreach ($form->getFields() as $field) {
-        if ($field->get('private'))
+        if (!$field->isVisibleToUsers())
             continue;
         ?>
         <tr>
diff --git a/include/client/tickets.inc.php b/include/client/tickets.inc.php
index 2c7a9f2317743e6ff170236a06ec39ce2355df23..2c1a04a97c208e2bdadc6281cca2ee0cd093e9c0 100644
--- a/include/client/tickets.inc.php
+++ b/include/client/tickets.inc.php
@@ -6,20 +6,26 @@ $status=null;
 if(isset($_REQUEST['status'])) { //Query string status has nothing to do with the real status used below.
     $qstr.='status='.urlencode($_REQUEST['status']);
     //Status we are actually going to use on the query...making sure it is clean!
+    $status=strtolower($_REQUEST['status']);
     switch(strtolower($_REQUEST['status'])) {
      case 'open':
+		$results_type=__('Open Tickets');
      case 'closed':
-        $status=strtolower($_REQUEST['status']);
+		$results_type=__('Closed Tickets');
+        break;
+     case 'resolved':
+        $results_type=__('Resolved Tickets');
         break;
      default:
         $status=''; //ignore
     }
 } elseif($thisclient->getNumOpenTickets()) {
     $status='open'; //Defaulting to open
+	$results_type=__('Open Tickets');
 }
 
-$sortOptions=array('id'=>'`number`', 'subject'=>'subject.value',
-                    'status'=>'ticket.status', 'dept'=>'dept_name','date'=>'ticket.created');
+$sortOptions=array('id'=>'`number`', 'subject'=>'cdata.subject',
+                    'status'=>'status.name', 'dept'=>'dept_name','date'=>'ticket.created');
 $orderWays=array('DESC'=>'DESC','ASC'=>'ASC');
 //Sorting options...
 $order_by=$order=null;
@@ -39,27 +45,26 @@ $x=$sort.'_sort';
 $$x=' class="'.strtolower($order).'" ';
 
 $qselect='SELECT ticket.ticket_id,ticket.`number`,ticket.dept_id,isanswered, '
-    .'dept.ispublic, subject.value as subject,'
-    .'dept_name,ticket. status, ticket.source, ticket.created ';
-
-$dynfields='(SELECT entry.object_id, value FROM '.FORM_ANSWER_TABLE.' ans '.
-         'LEFT JOIN '.FORM_ENTRY_TABLE.' entry ON entry.id=ans.entry_id '.
-         'LEFT JOIN '.FORM_FIELD_TABLE.' field ON field.id=ans.field_id '.
-         'WHERE field.name = "%1$s" AND entry.object_type="T")';
-$subject_sql = sprintf($dynfields, 'subject');
+    .'dept.ispublic, cdata.subject,'
+    .'dept_name, status.name as status, status.state, ticket.source, ticket.created ';
 
 $qfrom='FROM '.TICKET_TABLE.' ticket '
+      .' LEFT JOIN '.TICKET_STATUS_TABLE.' status
+            ON (status.id = ticket.status_id) '
+      .' LEFT JOIN '.TABLE_PREFIX.'ticket__cdata cdata ON (cdata.ticket_id = ticket.ticket_id)'
       .' LEFT JOIN '.DEPT_TABLE.' dept ON (ticket.dept_id=dept.dept_id) '
       .' LEFT JOIN '.TICKET_COLLABORATOR_TABLE.' collab
         ON (collab.ticket_id = ticket.ticket_id
-                AND collab.user_id ='.$thisclient->getId().' )'
-      .' LEFT JOIN '.$subject_sql.' subject ON ticket.ticket_id = subject.object_id ';
+                AND collab.user_id ='.$thisclient->getId().' )';
 
 $qwhere = sprintf(' WHERE ( ticket.user_id=%d OR collab.user_id=%d )',
             $thisclient->getId(), $thisclient->getId());
 
-if($status){
-    $qwhere.=' AND ticket.status='.db_input($status);
+$states = array(
+        'open' => 'open',
+        'closed' => 'closed');
+if($status && isset($states[$status])){
+    $qwhere.=' AND status.state='.db_input($states[$status]);
 }
 
 $search=($_REQUEST['a']=='search' && $_REQUEST['q']);
@@ -70,7 +75,7 @@ if($search) {
     } else {//Deep search!
         $queryterm=db_real_escape($_REQUEST['q'],false); //escape the term ONLY...no quotes.
         $qwhere.=' AND ( '
-                ." subject.value LIKE '%$queryterm%'"
+                ." cdata.subject LIKE '%$queryterm%'"
                 ." OR thread.body LIKE '%$queryterm%'"
                 .' ) ';
         $deep_search=true;
@@ -80,6 +85,8 @@ if($search) {
     }
 }
 
+TicketForm::ensureDynamicDataView();
+
 $total=db_count('SELECT count(DISTINCT ticket.ticket_id) '.$qfrom.' '.$qwhere);
 $page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1;
 $pageNav=new Pagenate($total, $page, PAGE_LIMIT);
@@ -94,53 +101,58 @@ $query="$qselect $qfrom $qwhere $qgroup ORDER BY $order_by $order LIMIT ".$pageN
 //echo $query;
 $res = db_query($query);
 $showing=($res && db_num_rows($res))?$pageNav->showing():"";
-$showing.=($status)?(' '.ucfirst($status).' Tickets'):' All Tickets';
+if(!$results_type)
+{
+	$results_type=ucfirst($status).' Tickets';
+}
+$showing.=($status)?(' '.$results_type):' '.__('All Tickets');
 if($search)
-    $showing="Search Results: $showing";
+    $showing=__('Search Results').": $showing";
 
 $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting
 
 ?>
-<h1>Tickets</h1>
+<h1><?php echo __('Tickets');?></h1>
 <br>
 <form action="tickets.php" method="get" id="ticketSearchForm">
     <input type="hidden" name="a"  value="search">
     <input type="text" name="q" size="20" value="<?php echo Format::htmlchars($_REQUEST['q']); ?>">
     <select name="status">
-        <option value="">&mdash; Any Status &mdash;</option>
+        <option value="">&mdash; <?php echo __('Any Status');?> &mdash;</option>
         <option value="open"
-            <?php echo ($status=='open')?'selected="selected"':'';?>>Open (<?php echo $thisclient->getNumOpenTickets(); ?>)</option>
+            <?php echo ($status=='open') ? 'selected="selected"' : '';?>>
+            <?php echo _P('ticket-status', 'Open');?> (<?php echo $thisclient->getNumOpenTickets(); ?>)</option>
         <?php
         if($thisclient->getNumClosedTickets()) {
             ?>
         <option value="closed"
-            <?php echo ($status=='closed')?'selected="selected"':'';?>>Closed (<?php echo $thisclient->getNumClosedTickets(); ?>)</option>
+            <?php echo ($status=='closed') ? 'selected="selected"' : '';?>>
+            <?php echo __('Closed');?> (<?php echo $thisclient->getNumClosedTickets(); ?>)</option>
         <?php
         } ?>
     </select>
-    <input type="submit" value="Go">
+    <input type="submit" value="<?php echo __('Go');?>">
 </form>
-<a class="refresh" href="<?php echo Format::htmlchars($_SERVER['REQUEST_URI']); ?>">Refresh</a>
+<a class="refresh" href="<?php echo Format::htmlchars($_SERVER['REQUEST_URI']); ?>"><?php echo __('Refresh'); ?></a>
 <table id="ticketTable" width="800" border="0" cellspacing="0" cellpadding="0">
     <caption><?php echo $showing; ?></caption>
     <thead>
         <tr>
-            <th width="70" nowrap>
-                <a href="tickets.php?sort=ID&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Ticket ID">Ticket #</a>
+            <th nowrap>
+                <a href="tickets.php?sort=ID&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Ticket ID"><?php echo __('Ticket #');?></a>
             </th>
-            <th width="100">
-                <a href="tickets.php?sort=date&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Date">Create Date</a>
+            <th width="120">
+                <a href="tickets.php?sort=date&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Date"><?php echo __('Create Date');?></a>
             </th>
-            <th width="80">
-                <a href="tickets.php?sort=status&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Status">Status</a>
+            <th width="100">
+                <a href="tickets.php?sort=status&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Status"><?php echo __('Status');?></a>
             </th>
-            <th width="300">
-                <a href="tickets.php?sort=subj&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Subject">Subject</a>
+            <th width="320">
+                <a href="tickets.php?sort=subj&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Subject"><?php echo __('Subject');?></a>
             </th>
-            <th width="150">
-                <a href="tickets.php?sort=dept&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Department">Department</a>
+            <th width="120">
+                <a href="tickets.php?sort=dept&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Department"><?php echo __('Department');?></a>
             </th>
-            <th width="100">Phone Number</th>
         </tr>
     </thead>
     <tbody>
@@ -148,44 +160,40 @@ $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting
      if($res && ($num=db_num_rows($res))) {
         $defaultDept=Dept::getDefaultDeptName(); //Default public dept.
         while ($row = db_fetch_array($res)) {
-            $dept=$row['ispublic']?$row['dept_name']:$defaultDept;
+            $dept= $row['ispublic']? $row['dept_name'] : $defaultDept;
             $subject=Format::htmlchars(Format::truncate($row['subject'],40));
             if($row['attachments'])
                 $subject.='  &nbsp;&nbsp;<span class="Icon file"></span>';
 
             $ticketNumber=$row['number'];
-            if($row['isanswered'] && !strcasecmp($row['status'],'open')) {
+            if($row['isanswered'] && !strcasecmp($row['state'], 'open')) {
                 $subject="<b>$subject</b>";
                 $ticketNumber="<b>$ticketNumber</b>";
             }
-            $phone=Format::phone($row['phone']);
-            if($row['phone_ext'])
-                $phone.=' '.$row['phone_ext'];
             ?>
             <tr id="<?php echo $row['ticket_id']; ?>">
-                <td class="centered">
+                <td>
                 <a class="Icon <?php echo strtolower($row['source']); ?>Ticket" title="<?php echo $row['email']; ?>"
                     href="tickets.php?id=<?php echo $row['ticket_id']; ?>"><?php echo $ticketNumber; ?></a>
                 </td>
                 <td>&nbsp;<?php echo Format::db_date($row['created']); ?></td>
-                <td>&nbsp;<?php echo ucfirst($row['status']); ?></td>
+                <td>&nbsp;<?php echo $row['status']; ?></td>
                 <td>
                     <a href="tickets.php?id=<?php echo $row['ticket_id']; ?>"><?php echo $subject; ?></a>
                 </td>
                 <td>&nbsp;<?php echo Format::truncate($dept,30); ?></td>
-                <td><?php echo $phone; ?></td>
             </tr>
         <?php
         }
 
      } else {
-         echo '<tr><td colspan="7">Your query did not match any records</td></tr>';
+         echo '<tr><td colspan="6">'.__('Your query did not match any records').'</td></tr>';
      }
     ?>
     </tbody>
 </table>
 <?php
 if($res && $num>0) {
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 }
 ?>
diff --git a/include/client/view.inc.php b/include/client/view.inc.php
index 2f7d487ae071631e3b8e0d371b92330423ce2c59..dc3a0419f97a353471b6a87f7dc4ad1453b5b881 100644
--- a/include/client/view.inc.php
+++ b/include/client/view.inc.php
@@ -4,6 +4,10 @@ if(!defined('OSTCLIENTINC') || !$thisclient || !$ticket || !$ticket->checkUserAc
 $info=($_POST && $errors)?Format::htmlchars($_POST):array();
 
 $dept = $ticket->getDept();
+
+if ($ticket->isClosed() && !$ticket->isReopenable())
+    $warn = __('This ticket is marked as closed and cannot be reopened.');
+
 //Making sure we don't leak out internal dept names
 if(!$dept || !$dept->isPublic())
     $dept = $cfg->getDefaultDept();
@@ -13,12 +17,13 @@ if ($thisclient && $thisclient->isGuest()
 
 <div id="msg_info">
     <i class="icon-compass icon-2x pull-left"></i>
-    <strong>Looking for your other tickets?</strong></br>
+    <strong><?php echo __('Looking for your other tickets?'); ?></strong></br>
     <a href="<?php echo ROOT_PATH; ?>login.php?e=<?php
         echo urlencode($thisclient->getEmail());
-        ?>" style="text-decoration:underline">Sign in</a> or
-    <a href="account.php?do=create" style="text-decoration:underline">register for an account</a>
-    for the best experience on our help desk.</div>
+    ?>" style="text-decoration:underline"><?php echo __('Sign In'); ?></a>
+    <?php echo sprintf(__('or %s register for an account %s for the best experience on our help desk.'),
+        '<a href="account.php?do=create" style="text-decoration:underline">','</a>'); ?>
+    </div>
 
 <?php } ?>
 
@@ -26,12 +31,12 @@ if ($thisclient && $thisclient->isGuest()
     <tr>
         <td colspan="2" width="100%">
             <h1>
-                Ticket #<?php echo $ticket->getNumber(); ?> &nbsp;
+                <?php echo sprintf(__('Ticket #%s'), $ticket->getNumber()); ?> &nbsp;
                 <a href="tickets.php?id=<?php echo $ticket->getId(); ?>" title="Reload"><span class="Icon refresh">&nbsp;</span></a>
 <?php if ($cfg->allowClientUpdates()
         // Only ticket owners can edit the ticket details (and other forms)
         && $thisclient->getId() == $ticket->getUserId()) { ?>
-                <a class="action-button" href="tickets.php?a=edit&id=<?php
+                <a class="action-button pull-right" href="tickets.php?a=edit&id=<?php
                      echo $ticket->getId(); ?>"><i class="icon-edit"></i> Edit</a>
 <?php } ?>
             </h1>
@@ -41,15 +46,15 @@ if ($thisclient && $thisclient->isGuest()
         <td width="50%">
             <table class="infoTable" cellspacing="1" cellpadding="3" width="100%" border="0">
                 <tr>
-                    <th width="100">Ticket Status:</th>
-                    <td><?php echo ucfirst($ticket->getStatus()); ?></td>
+                    <th width="100"><?php echo __('Ticket Status');?>:</th>
+                    <td><?php echo $ticket->getStatus(); ?></td>
                 </tr>
                 <tr>
-                    <th>Department:</th>
+                    <th><?php echo __('Department');?>:</th>
                     <td><?php echo Format::htmlchars($dept instanceof Dept ? $dept->getName() : ''); ?></td>
                 </tr>
                 <tr>
-                    <th>Create Date:</th>
+                    <th><?php echo __('Create Date');?>:</th>
                     <td><?php echo Format::db_datetime($ticket->getCreateDate()); ?></td>
                 </tr>
            </table>
@@ -57,15 +62,15 @@ if ($thisclient && $thisclient->isGuest()
        <td width="50%">
            <table class="infoTable" cellspacing="1" cellpadding="3" width="100%" border="0">
                <tr>
-                   <th width="100">Name:</th>
-                   <td><?php echo ucfirst(Format::htmlchars($ticket->getName())); ?></td>
+                   <th width="100"><?php echo __('Name');?>:</th>
+                   <td><?php echo mb_convert_case(Format::htmlchars($ticket->getName()), MB_CASE_TITLE); ?></td>
                </tr>
                <tr>
-                   <th width="100">Email:</th>
+                   <th width="100"><?php echo __('Email');?>:</th>
                    <td><?php echo Format::htmlchars($ticket->getEmail()); ?></td>
                </tr>
                <tr>
-                   <th>Phone:</th>
+                   <th><?php echo __('Phone');?>:</th>
                    <td><?php echo $ticket->getPhoneNumber(); ?></td>
                </tr>
             </table>
@@ -97,7 +102,7 @@ foreach (DynamicFormEntry::forTicket($ticket->getId()) as $idx=>$form) {
 </tr>
 </table>
 <br>
-<div class="subject">Subject: <strong><?php echo Format::htmlchars($ticket->getSubject()); ?></strong></div>
+<div class="subject"><?php echo __('Subject'); ?>: <strong><?php echo Format::htmlchars($ticket->getSubject()); ?></strong></div>
 <div id="ticketThread">
 <?php
 if($ticket->getThreadCount() && ($thread=$ticket->getClientThread())) {
@@ -145,9 +150,13 @@ if($ticket->getThreadCount() && ($thread=$ticket->getClientThread())) {
 <?php }elseif($warn) { ?>
     <div id="msg_warning"><?php echo $warn; ?></div>
 <?php } ?>
+
+<?php
+
+if (!$ticket->isClosed() || $ticket->isReopenable()) { ?>
 <form id="reply" action="tickets.php?id=<?php echo $ticket->getId(); ?>#reply" name="reply" method="post" enctype="multipart/form-data">
     <?php csrf_token(); ?>
-    <h2>Post a Reply</h2>
+    <h2><?php echo __('Post a Reply');?></h2>
     <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>">
     <input type="hidden" name="a" value="reply">
     <table border="0" cellspacing="0" cellpadding="3" style="width:100%">
@@ -155,9 +164,9 @@ if($ticket->getThreadCount() && ($thread=$ticket->getClientThread())) {
             <td colspan="2">
                 <?php
                 if($ticket->isClosed()) {
-                    $msg='<b>Ticket will be reopened on message post</b>';
+                    $msg='<b>'.__('Ticket will be reopened on message post').'</b>';
                 } else {
-                    $msg='To best assist you, please be specific and detailed';
+                    $msg=__('To best assist you, we request that you be specific and detailed');
                 }
                 ?>
                 <span id="msg"><em><?php echo $msg; ?> </em></span><font class="error">*&nbsp;<?php echo $errors['message']; ?></font>
@@ -166,28 +175,21 @@ if($ticket->getThreadCount() && ($thread=$ticket->getClientThread())) {
                     data-draft-namespace="ticket.client"
                     data-draft-object-id="<?php echo $ticket->getId(); ?>"
                     class="richtext ifhtml draft"><?php echo $info['message']; ?></textarea>
-            </td>
-        </tr>
         <?php
-        if($cfg->allowOnlineAttachments()) { ?>
-        <tr>
-            <td width="160">
-                <label for="attachment">Attachments:</label>
-            </td>
-            <td width="640" id="reply_form_attachments" class="attachments">
-                <div class="uploads">
-                </div>
-                <div class="file_input">
-                    <input class="multifile" type="file" name="attachments[]" size="30" value="" />
-                </div>
-            </td>
-        </tr>
+        if ($messageField->isAttachmentsEnabled()) { ?>
+<?php
+            print $attachments->render(true);
+?>
         <?php
         } ?>
+            </td>
+        </tr>
     </table>
     <p style="padding-left:165px;">
-        <input type="submit" value="Post Reply">
-        <input type="reset" value="Reset">
-        <input type="button" value="Cancel" onClick="history.go(-1)">
+        <input type="submit" value="<?php echo __('Post Reply');?>">
+        <input type="reset" value="<?php echo __('Reset');?>">
+        <input type="button" value="<?php echo __('Cancel');?>" onClick="history.go(-1)">
     </p>
 </form>
+<?php
+} ?>
diff --git a/include/config/filetype.yaml b/include/config/filetype.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..dafd40109f0ece9aca82de1835373b36f652bed3
--- /dev/null
+++ b/include/config/filetype.yaml
@@ -0,0 +1,129 @@
+---
+image:
+  description: Images
+  types:
+    'image/bmp': ['bmp']
+    'image/gif': ['gif']
+    'image/jpeg': ['jpeg', 'jpg']
+    'image/png': ['png']
+    'image/svg+xml': ['svg']
+    'image/tiff': ['tiff']
+    'image/vnd.adobe.photoshop': ['psd']
+    'image/vnd.microsoft.icon': ['ico']
+    'image/x-ico': ['ico']
+    'application/postscript': ['eps']
+audio:
+  description: Audio and Music
+  types:
+    'audio/aiff': []
+    'audio/mpeg': ['mp3']
+    'audio/mp4': ['m4a', 'm4r', 'm4p']
+    'audio/ogg': ['ogg']
+    'audio/vorbis',
+    'audio/vnd.wav': ['wav']
+    'audio/wav': ['wav']
+    'audio/x-midi': ['mid', 'midi']
+text:
+  description: Text Documents
+  types:
+    'text/css': ['css']
+    'text/html': ['htm', 'html']
+    'text/javascript': ['js']
+    'text/plain': ['txt']
+    'text/xml': ['xml']
+    'application/json': ['json']
+    'application/javascript': ['js']
+office:
+  description: Common Office Documents
+  types:
+    # Microsoft Office
+    'application/msword': ['doc']
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['docx']
+    'application/vnd.ms-word.document.macroEnabled.12': ['docm']
+    'application/vnd.ms-excel': ['xls']
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['xlsx']
+    'application/vnd.ms-excel.sheet.macroEnabled.12': ['xlsm']
+    'application/vnd.ms-excel.sheet.binary.macroEnabled.12': ['xlsb']
+    'application/vnd.ms-powerpoint': ['ppt']
+    'application/vnd.openxmlformats-officedocument.presentationml.presentation': ['pptx']
+    'application/vnd.openxmlformats-officedocument.presentationml.slideshow': ['ppsx']
+    'application/vnd.ms-powerpoint.presentation.macroEnabled.12': ['pptm']
+    'application/vnd.ms-powerpoint.slideshow.macroEnabled.12': ['ppsm']
+    'application/vnd.ms-access': ['mdb', 'accdb']
+    'application/vnd.ms-project': []
+    'application/msonenote': []
+    'application/vnd.ms-publisher': []
+    'application/rtf': ['rtf']
+    'application/vnd.ms-works': []
+
+    # iWork
+    'application/vnd.apple.keynote': ['keynote']
+    'application/vnd.apple.pages': ['pages']
+    'application/vnd.apple.numbers': ['numbers']
+
+    # OpenOffice
+    'application/vnd.oasis.opendocument.text': []
+    'application/vnd.oasis.opendocument.text-web': []
+    'application/vnd.oasis.opendocument.text-master': []
+    'application/vnd.oasis.opendocument.graphics': []
+    'application/vnd.oasis.opendocument.presentation': []
+    'application/vnd.oasis.opendocument.spreadsheet': []
+    'application/vnd.oasis.opendocument.chart': []
+    'application/vnd.oasis.opendocument.formula': []
+    'application/vnd.oasis.opendocument.database': []
+    'application/vnd.oasis.opendocument.image': []
+    'application/vnd.openofficeorg.extension': []
+
+    # Other office
+    'application/wordperfect': []
+    'application/vnd.kde.karbon': []
+    'application/vnd.kde.kchart': []
+    'application/vnd.kde.kformula': []
+    'application/vnd.kde.kivio': []
+    'application/vnd.kde.kontour': []
+    'application/vnd.kde.kpresenter': []
+    'application/vnd.kde.kspread': []
+    'application/vnd.kde.kword': []
+
+    # Creative / Common
+    'application/pdf': ['pdf']
+    '.csv': ['csv']
+    'application/illustrator': ['ai']
+    'application/x-director': []
+    'application/x-indesign': []
+
+    # Interchange
+    'text/vcard': []
+
+    # Other
+    'image/x-dwg': ['dwg']
+    'image/vnd.dwg': ['dwg']
+    'image/vnd.dxf': ['dxf']
+    'application/x-autocad': []
+    'application/x-mathcad': []
+    'application/x-msmoney': []
+
+    'application/x-latex': ['tex']
+video:
+  description: Video Files
+  types:
+    'video/avi': ['avi']
+    'video/mpeg': ['mpg','mpeg']
+    'video/mp4': ['mp4']
+    'video/ogg': ['ogg']
+    'video/quicktime': []
+    'video/webm': []
+    'video/x-ms-asf': []
+    'video/x-ms-wmv': []
+    'application/x-dvi': ['dvi']
+    'application/x-shockwave-flash': ['swf']
+archive:
+  description: Archives
+  types:
+    'application/tar': ['tar']
+    'application/gzip': ['gz']
+    'application/x-lha': []
+    'application/rar': ['rar']
+    'application/x-compress': ['z']
+    'application/zip': ['zip']
+    'application/x-7z-compressed': ['7z']
diff --git a/include/i18n/README.md b/include/i18n/README.md
index 5cc60a15e3e727cd07548d44ad6ee55ed5a8c216..2bf307b304c5349a3781857a1b8741224e893c5a 100644
--- a/include/i18n/README.md
+++ b/include/i18n/README.md
@@ -22,10 +22,5 @@ Comments are meant to serve as a consistent hint to the context of the text.
 
 Starting a new translation
 --------------------------
-Copy the entire en_US folder and all its contents and subfolders to a new
-language code, such as de_DE. This is recommended as opposed to copying the
-contents of another translation, because the en_US folder is the most
-up-to-date in terms of content. osTicket is developed with en_US as the
-primary language. Therefore new features will be implemented against the
-en_US data first. If you are beginning a new translation, start with the
-most current texts.
+We are using Crowdin to manage our translations. Visit our translation page
+at http://i18n.crowdin.com/
diff --git a/include/i18n/en_US/config.yaml b/include/i18n/en_US/config.yaml
index 9d1e385c3bd979cf2bd12dcfeb6f6f9aa874085b..a63764ba9c1b3b1aeb8b56488c0cbc6c4aa4409e 100644
--- a/include/i18n/en_US/config.yaml
+++ b/include/i18n/en_US/config.yaml
@@ -78,7 +78,8 @@ core:
     hide_staff_name: 0
     overlimit_notice_active: 0
     email_attachments: 1
-    random_ticket_ids: 1
+    number_format: '######'
+    sequence_id: 0
     log_level: 2
     log_graceperiod: 12
     client_registration: 'public'
diff --git a/include/i18n/en_US/form.yaml b/include/i18n/en_US/form.yaml
index fe4be2f66098d0c8de9daee25c785ed3d04260f9..b5bde9db9af53ae3189a77080db5085e649184cf 100644
--- a/include/i18n/en_US/form.yaml
+++ b/include/i18n/en_US/form.yaml
@@ -41,7 +41,6 @@
         size: 40
         length: 64
         validator: email # notrans
-
     - type: text # notrans
       name: name # notrans
       label: Full Name
@@ -51,13 +50,11 @@
       configuration:
         size: 40
         length: 64
-
     - type: phone # notrans
       name: phone # notrans
       label: Phone Number
       required: false
       sort: 3
-
     - type: memo # notrans
       name: notes
       label: Internal Notes
@@ -67,7 +64,6 @@
       configuration:
         rows: 4
         cols: 40
-
 - id: 2
   type: T # notrans
   title: Ticket Details
@@ -88,7 +84,6 @@
       configuration:
         size: 40
         length: 50
-
     - id: 21
       type: thread # notrans
       name: message # notrans
@@ -97,7 +92,6 @@
       required: true
       edit_mask: 15
       sort: 2
-
     - id: 22
       type: priority # notrans
       name: priority # notrans
@@ -106,7 +100,6 @@
       private: true
       edit_mask: 3
       sort: 3
-
 - type: C # notrans
   title: Company Information
   instructions: Details available in email templates
@@ -121,7 +114,6 @@
       configuration:
         size: 40
         length: 64
-
     - type: text # notrans
       name: website # notrans
       label: Website
@@ -129,7 +121,6 @@
       configuration:
         size: 40
         length: 64
-
     - type: phone # notrans
       name: phone # notrans
       label: Phone Number
@@ -137,7 +128,6 @@
       sort: 3
       configuration:
         ext: false
-
     - type: memo # notrans
       name: address
       label: Address
@@ -148,7 +138,6 @@
         cols: 40
         html: false
         length: 100
-
 - type: O # notrans
   title: Organization Information
   instructions: Details on user organization
@@ -163,7 +152,6 @@
       configuration:
         size: 40
         length: 64
-
     - type: memo
       name: address
       label: Address
@@ -174,13 +162,11 @@
         cols: 40
         length: 100
         html: false
-
     - type: phone
       name: phone
       label: Phone
       required: false
       sort: 3
-
     - type: text
       name: website
       label: Website
@@ -189,7 +175,6 @@
       configuration:
         size: 40
         length: 0
-
     - type: memo # notrans
       name: notes
       label: Internal Notes
diff --git a/include/i18n/en_US/help/tips/dashboard.dashboard.yaml b/include/i18n/en_US/help/tips/dashboard.dashboard.yaml
index 98603045dd488706eeb46d12b38f6fc3236ba267..5ce715401b076ef151a2fa5a7cac3bf4aa115a09 100644
--- a/include/i18n/en_US/help/tips/dashboard.dashboard.yaml
+++ b/include/i18n/en_US/help/tips/dashboard.dashboard.yaml
@@ -90,31 +90,3 @@ response_time:
         response. This measurement of time is not exclusive to a Client’s
         correspondence of the initial Ticket opening. This refers to every act of
         discourse originating with a Client.
-
-export:
-    title: Export
-    content: >
-        You may export the data currently in view. Therefore,  the data that is
-        exported will be a direct reflection of whatever date span and specific
-        tabbed subject is currently specified when an <span
-        class="doc-desc-title">Export</span> is requested. The file format is <span
-        class="doc-desc-title">.csv</span>, which is compatible to any spreadsheet
-        application.
-
-department:
-    title: Department
-    content: >
-        Choose this tab if you would like to see the Data of each column that is
-        specific to your Departments.
-
-topics:
-    title: Topics
-    content: >
-        Choose this tab if you would like to see the Data of each column that is
-        specific to your Topics.
-
-agents_staff:
-    title: Agents (Staff)
-    content: >
-        Choose this tab if you would like to see the Data of each column that is
-        specific to your individual Agents.
diff --git a/include/i18n/en_US/help/tips/emails.email.yaml b/include/i18n/en_US/help/tips/emails.email.yaml
index 5735bdbb638a7b4ec11700ed9f2818fa89b59b30..b261c10eef61fd6e029fcafcd795d7226a2719fd 100644
--- a/include/i18n/en_US/help/tips/emails.email.yaml
+++ b/include/i18n/en_US/help/tips/emails.email.yaml
@@ -30,7 +30,7 @@ new_ticket_priority:
     title: New Ticket Priority
     content: >
         Choose the <span class="doc-desc-title">priority</span> for new
-        tickets created via ths Email Address.
+        tickets created via this Email Address.
 
 new_ticket_department:
     title: New Ticket Department
diff --git a/include/i18n/en_US/help/tips/forms.yaml b/include/i18n/en_US/help/tips/forms.yaml
index 020bb06911a5cf1594a05f79092d9a6d41144221..2731df424f35bedeee82458aa6f3582da22c0f0a 100644
--- a/include/i18n/en_US/help/tips/forms.yaml
+++ b/include/i18n/en_US/help/tips/forms.yaml
@@ -48,21 +48,29 @@ field_type:
       - title: Custom Lists
         href: /scp/lists.php
 
-field_internal:
+field_visibility:
     title: Field Visibility
     content: >
-        Fields marked internal are hidden from your clients. Use internal
-        fields to track things which only your staff need to access.
-
-field_required:
-    title: Data Requirement
-    content: >
-        Forms that have required fields must have valid data before the form
-        can be saved. If checked, forms cannot be submitted or saved until all
-        required fields are satisfied.<br>
-        <br>
-        Internal fields can only be required of staff members, since they
-        are hidden from clients.
+        Choose a visibility and requirement option for this field.
+        <table border="1" cellpadding="2px" cellspacing="0" style="margin-top:7px"
+            ><tbody style="vertical-align:top;">
+            <tr><th>Setting</th>
+                <th>Result</th></tr>
+            <tr><td>Optional</td>
+                <td>Agents and EndUsers can see the field, but neither is required to answer.</td></tr>
+            <tr><td>Required</td>
+                <td>Agents and EndUsers can see the field, and both are required to answer</td></tr>
+            <tr><td>Required for EndUsers</td>
+                <td>Agents and EndUsers can see the field, only EndUsers are required to answer</td></tr>
+            <tr><td>Required for Agents</td>
+                <td>Agents and EndUsers can see the field, only Agents are required to answer</td></tr>
+            <tr><td>Internal, Optional</td>
+                <td>Only Agents can see the field, but no answer is required.</td></tr>
+            <tr><td>Internal, Required</td>
+                <td>Only Agents can see the field, and an answer is required.</td></tr>
+            <tr><td>For EndUsers Only</td>
+                <td>Only EndUsers can see the field, and an answer is required.</td></tr>
+        </tbody></table>
 
 field_variable:
     title: Field Automation
diff --git a/include/i18n/en_US/help/tips/knowledgebase.canned_response.yaml b/include/i18n/en_US/help/tips/knowledgebase.canned_response.yaml
index 67a39c729c6ea90e557557a1964cd429b999fc90..0779b10feb14d235a128d2e4f9b36388600fb332 100644
--- a/include/i18n/en_US/help/tips/knowledgebase.canned_response.yaml
+++ b/include/i18n/en_US/help/tips/knowledgebase.canned_response.yaml
@@ -16,34 +16,14 @@
 canned_response:
     title: Canned Response
     content: >
-        Create a <span class="doc-desc-title">Canned Response</span> that can 
-        be included in a ticket response. In fact, you may include as many 
-        <span class="doc-desc-title">Canned Responses</span> as you would like
-        in a ticket response. It can also be included in the <span 
-        class="doc-desc-title">Ticket Auto-Response</span> setup by a <span 
+        Create a <span class="doc-desc-title">Canned Response</span> that can
+        be included in a ticket response. It can also be used as a <span
+        class="doc-desc-title">Ticket Auto-Response</span> setup by a <span
         class="doc-desc-title">Ticket Filter</span>.
     links:
       - title: Setup a Ticket Filter
         href: /scp/filters.php
 
-canned_response_settings:
-    title: Canned Response Settings
-    content: >
-
-status:
-    title: Status
-    content: >
-        Disable your new <span class="doc-desc-title">Canned Response</span>
-        if you do not want it to be available to Agents immediately upon creation.
-        
-department:
-    title: Department
-    content: >
-
-title:
-    title: Title
-    content: >
-
 canned_attachments:
     title: Canned Attachments
     content: >
diff --git a/include/i18n/en_US/help/tips/knowledgebase.category.yaml b/include/i18n/en_US/help/tips/knowledgebase.category.yaml
index 1f22329bb4e20981e2ff94782093083283e03382..a1bbcbcee752f0cee08108a6f00463c9aefdd365 100644
--- a/include/i18n/en_US/help/tips/knowledgebase.category.yaml
+++ b/include/i18n/en_US/help/tips/knowledgebase.category.yaml
@@ -13,22 +13,3 @@
 # must match the HTML #ids put into the page template.
 #
 ---
-category_information:
-    title: Category Information
-    content: >
-        Once you have created categories, you can select the category that the FAQ
-        pertains to then select “Add New FAQ”.  Type in the Question that will be
-        visible, if the FAQ will be Public or Internal and type the Answer in the
-        text box. You can also attach a file to the FAQ.
-
-category_type:
-    title: Category Type
-    content: >
-
-category_name:
-    title: Category Name
-    content: >
-
-category_description:
-    title: Category Description
-    content: >
diff --git a/include/i18n/en_US/help/tips/manage.helptopic.yaml b/include/i18n/en_US/help/tips/manage.helptopic.yaml
index d2179170cea229fe773d361c85080c4e15c73188..0297f1ceeb93f418b112cdfecbc4213f1ae2cbeb 100644
--- a/include/i18n/en_US/help/tips/manage.helptopic.yaml
+++ b/include/i18n/en_US/help/tips/manage.helptopic.yaml
@@ -112,3 +112,10 @@ ticket_auto_response:
     links:
       - title: Autoresponder Settings
         href: /scp/settings.php?t=autoresp
+
+custom_numbers:
+    title: Custom Ticket Numbers
+    content: >
+        Choose "Custom" here to override the system default ticket numbering
+        format for tickets created in this help topic.  See the help tips on
+        the Settings / Tickets page for more details on the settings.
diff --git a/include/i18n/en_US/help/tips/settings.kb.yaml b/include/i18n/en_US/help/tips/settings.kb.yaml
index ea3cb117e1cfeba21b81b6a88df49b034138ff83..12f4954725fd56d5befbda7ab26276487f7d1746 100644
--- a/include/i18n/en_US/help/tips/settings.kb.yaml
+++ b/include/i18n/en_US/help/tips/settings.kb.yaml
@@ -23,7 +23,7 @@ knowledge_base_status:
         Enable this setting to allow your users self-service access to
         your public knowledge base articles.
         <br><br>
-        Knowledge base categories and FAQs can be made internal (viewable only by Agents.)
+        Knowledge base categories and FAQs can be made internal (viewable only by Agents).
     links:
       - title: Manage Knowledge Base
         href: /scp/kb.php
diff --git a/include/i18n/en_US/help/tips/settings.pages.yaml b/include/i18n/en_US/help/tips/settings.pages.yaml
index a506e338509ad0a6d0970f127f4e282af5cf15c0..487d09cf09f792374742136de279d0a338eff98a 100644
--- a/include/i18n/en_US/help/tips/settings.pages.yaml
+++ b/include/i18n/en_US/help/tips/settings.pages.yaml
@@ -28,7 +28,7 @@ landing_page:
     title: Landing Page
     content: >
         The <span class="doc-desc-title">Landing Page</span> is displayed on
-        the front page of your your support site.
+        the front page of your support site.
 
 offline_page:
     title: Offline Page
diff --git a/include/i18n/en_US/help/tips/settings.ticket.yaml b/include/i18n/en_US/help/tips/settings.ticket.yaml
index ee516df2f50a187771161b08ae9cc5844d57e33a..3c7b872046a878bf15c964f6d65882d43deea241 100644
--- a/include/i18n/en_US/help/tips/settings.ticket.yaml
+++ b/include/i18n/en_US/help/tips/settings.ticket.yaml
@@ -13,6 +13,35 @@
 # must match the HTML #ids put into the page template.
 #
 ---
+number_format:
+    title: Ticket Number Format
+    content: >
+        This setting is used to generate ticket numbers. Use hash signs
+        (`#`) where digits are to be placed. Any other text in the number
+        format will be preserved. <span class="doc-desc-title">Help
+        Topics</span> can define custom number formats.
+        <br/><br/>
+        For example, for six-digit numbers, use <code>######</code>.
+
+sequence_id:
+    title: Ticket Number Sequence
+    content: >
+        Choose a sequence from which to derive new ticket numbers. The
+        system has a incrementing sequence and a random sequence by default.
+        You may create as many sequences as you wish. Use various sequences
+        in the <span class="doc-desc-title">Ticket Number
+        Format</span> configuration for help topics.
+
+default_ticket_status:
+    title: Default Status for new Tickets
+    content: >
+        Choose a status as the default for new tickets. This can be defined
+        for each help topic, if desired. It can also be overridden by a
+        ticket filter.
+    links:
+      - title: Manage Ticket Statuses
+        href: /scp/lists.php?type=ticket-status
+
 default_sla:
     title: Default SLA
     content: >
@@ -28,7 +57,7 @@ default_priority:
         Choose a default <span class="doc-desc-title">priority</span> for
         tickets not assigned a priority automatically.
         <br/><br/>
-        Prioriy can be assigned via the help topic, routed department, or
+        Priority can be assigned via the help topic, routed department, or
         ticket filter settings.
 
 maximum_open_tickets:
@@ -99,40 +128,23 @@ enable_html_ticket_thread:
         If enabled, this will permit the use of rich text formatting between
         Clients and Agents.
 
-attachments:
-    title: Attachments
-    content: >
-
-allow_attachments:
-    title: Allow Attachments
+ticket_attachment_settings:
+    title: Ticket Thread Attachments
     content: >
+        Configure settings for files attached to the <span
+        class="doc-desc-title">issue details</span> field. These settings
+        are used for all new tickets and new messages regardless of the
+        source channel (web portal, email, api, etc.).
 
-emailed_api_attachments:
-    title: Emailed/API Attachments
-    content: >
-
-online_web_attachments:
-    title: Online/Web Attachments
-    content: >
-
-max_user_file_uploads:
-    title: Max. User File Uploads
-    content: >
-
-max_staff_file_uploads:
-    title: Max. Staff File Uploads
-    content: >
-
-maximum_file_size:
+max_file_size:
     title: Maximum File Size
     content: >
+        Choose a maximum file size for attachments uploaded by agents. This
+        includes canned attachments, knowledge base articles, and
+        attachments to ticket replies.
 
 ticket_response_files:
     title: Ticket Response Files
     content: >
         If enabled, any attachments an Agent may attach to a ticket response will
         be also included in the email to the User.
-
-accepted_file_types:
-    title: Accepted File Types
-    content: >
diff --git a/include/i18n/en_US/help/tips/staff.staff_members.yaml b/include/i18n/en_US/help/tips/staff.staff_members.yaml
index 5d7b8cbe8ded464b7d1c15c3615d193d4b3f98a8..882bfe9eed9dfc390461796270f0966dd4410c26 100644
--- a/include/i18n/en_US/help/tips/staff.staff_members.yaml
+++ b/include/i18n/en_US/help/tips/staff.staff_members.yaml
@@ -13,10 +13,6 @@
 # must match the HTML #ids put into the page template.
 #
 ---
-add_new_staff:
-    title: Add New Staff
-    content: >
-
 staff_members:
     title: Staff Members
     content: >
@@ -26,48 +22,8 @@ staff_members:
         broken up by pages. See the pages section below to navigate through more
         results.
 
-checkbox_column:
-    title: Manage Multiple Agents
-    content: >
-        By choosing a checkbox next to the name of the <span
-        class="doc-desc-title">Agent</span>(s), you may alter the <span
-        class="doc-desc-title">Account Status</span> to either Locked or Enabled.
-        If you change the <span class="doc-desc-title">agent</span>’s status to
-        Locked, then he/she will not be able to login.<br/>
-        <br/>
-        If you change the <span class="doc-desc-title">agent</span>’s status to
-        Enabled, then all normal account settings established for that <span
-        class="doc-desc-title">Agent</span> will resume.<br/>
-        <br/>
-        If you change the <span class="doc-desc-title">agent</span>’s status to
-        Locked, then he/she will not be able to login.
-
-name:
-    title: Name
-    content: >
-
-username:
-    title: Username
-    content: >
-
 status:
     title: Status
     content: >
         This will display whether the <span class="doc-desc-title">Agent</span> is
         Locked, Active, or Active (Vacation).
-
-group:
-    title: Group
-    content: >
-
-department:
-    title: Department
-    content: >
-
-created:
-    title: Created
-    content: >
-
-last_login:
-    title: Last Login
-    content: >
diff --git a/include/i18n/en_US/list.yaml b/include/i18n/en_US/list.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..613ca4a2f83db4a46048c70069a9569c95791a86
--- /dev/null
+++ b/include/i18n/en_US/list.yaml
@@ -0,0 +1,57 @@
+#
+# Custom (dynamic) lists. This data is used as the initial,
+# minimal data for dynamic list that ships with the system.
+#
+# Fields:
+# id:           Primary id (not recommended)
+# name:         Name of the list
+# name_plural:  Name in plural (optional)
+# sort_mode:    Sorting order (Alpha, -Alpha, SortCol)
+# masks:        Edit masks to indicate various statuses of the list
+#                (e.g  if editable or deletable..etc)
+# notes:        Notes for the list
+# items:        List of items for the list
+#   id:         Primary id
+#   value:      Value (name) of the list item
+#   extra:      Abbreviated version of the value
+#   status:     If enabled (1 - enabled, 0 - disabled)
+#   sort:       Sort order  (optional)
+#   properties: Item-specific config based on Ticket Flags form fields
+#     (key):  (value)
+# properties:   List properties form (see form.yaml for details)
+#
+---
+# Ticket statuses
+- type: ticket-status #notrans
+  name: Ticket Status
+  name_plural: Ticket Statuses
+  sort_mode: SortCol  # notrans
+  masks: 13
+  notes: |
+    Ticket statuses
+  properties:
+    title: Ticket Status Properties
+    instructions: Properties that can be set on a ticket status.
+    deletable: false
+    fields:
+      - type: state # notrans
+        name: state # notrans
+        label: State
+        required: true
+        sort: 1
+        edit_mask: 63
+        configuration:
+            prompt: State of a ticket
+      - type: memo # notrans
+        name: description # notrans
+        label: Description
+        required: false
+        sort: 3
+        edit_mask: 15
+        configuration:
+            rows: 2
+            cols: 40
+            html: false
+            length: 100
+  configuration:
+    handler: TicketStatusList
diff --git a/include/i18n/en_US/sequence.yaml b/include/i18n/en_US/sequence.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..bb502c3526c287280a2fe664419989e26c9307ef
--- /dev/null
+++ b/include/i18n/en_US/sequence.yaml
@@ -0,0 +1,23 @@
+#
+# Sequences installed with the system
+#
+# Fields:
+# id:           PK
+# name:         Name of the sequence
+# next:         Next value of the sequence
+# padding:      Padding character
+# increment:    Distance between two numbers of the sequence
+# flags:        Bitmask of flag settings. Currently known values are
+#               INTERNAL:=0x0001 (restrict delete)
+#
+---
+# ID:1 is reserved for upgrades. When transitioning to osTicket 1.10, the
+# sequence ID:1 will be configured to start counting from the current
+# MAX(ticket.ticket_id). The upgrade will miss this step if there is no
+# sequence with ID:1
+- id: 1
+  name: "General Tickets"
+  next: 1
+  padding: '0'
+  increment: 1
+  flags: 1
diff --git a/include/i18n/en_US/templates/page/landing.yaml b/include/i18n/en_US/templates/page/landing.yaml
index ceedc21a3bffc95c97b080da3006e5e00c6d0e1a..112bc512cb990403f26459f8004834f5fcadf022 100644
--- a/include/i18n/en_US/templates/page/landing.yaml
+++ b/include/i18n/en_US/templates/page/landing.yaml
@@ -7,7 +7,7 @@
 ---
 notes: >
     The Landing Page refers to the content of the Customer Portal's
-    intial view. The template modifies the content seen above the two links
+    initial view. The template modifies the content seen above the two links
     <strong>Open a New Ticket</strong> and <strong>Check Ticket Status</strong>.
 
 name: Landing
diff --git a/include/i18n/en_US/ticket_status.yaml b/include/i18n/en_US/ticket_status.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..1a5d268a405e86f8ba16f8107c9d3861febaddb3
--- /dev/null
+++ b/include/i18n/en_US/ticket_status.yaml
@@ -0,0 +1,68 @@
+#
+# Default system data for ticket statuses
+#
+# Fields:
+#  id - (int:optional) id number in the database
+#  name - (string) descriptive name of the status
+#  state - (string) Main status of a ticket
+#  (open, closed, archived, deleted)
+#  mode - (bit) access mask (1 - enabled, 2 - internal)
+#  flags - (bit) flags that can be set on a ticket
+#  properties:
+#   description - (string) Description of the status
+#
+---
+- id: 1
+  name: Open
+  state: open
+  mode: 3
+  sort: 1
+  flags: 0
+  properties:
+    description: >
+        Open tickets.
+
+- id: 2
+  name: Resolved
+  state: closed
+  mode: 1
+  sort: 2
+  flags: 0
+  properties:
+    allowreopen: true
+    reopenstatus: 0
+    description: >
+        Resolved tickets
+
+- id: 3
+  name: Closed
+  state: closed
+  mode: 3
+  sort: 3
+  flags: 0
+  properties:
+    allowreopen: true
+    reopenstatus: 0
+    description: >
+        Closed tickets. Tickets will still be accessible on client and staff panels.
+
+- id: 4
+  name: Archived
+  state:  archived
+  mode: 3
+  sort: 4
+  flags: 0
+  properties:
+    description: >
+        Tickets only adminstratively available but no longer accessible on
+        ticket queues and client panel.
+
+- id: 5
+  name: Deleted
+  state: deleted
+  mode: 3
+  sort: 5
+  flags: 0
+  properties:
+    description: >
+        Tickets queued for deletion. Not accessible on ticket queues.
diff --git a/include/i18n/langs.php b/include/i18n/langs.php
index 09957c42be514963c8d7a40f99051b996e728b42..f48058009fb70d51aabcd144716a8972ea8baa1a 100644
--- a/include/i18n/langs.php
+++ b/include/i18n/langs.php
@@ -29,6 +29,7 @@ return array(
         "nativeName" => "አማርኛ"
     ),
     "ar" => array(
+        "direction" => "rtl",
         "name" => "Arabic",
         "nativeName" => "العربية"
     ),
@@ -106,6 +107,7 @@ return array(
     ),
     "ca" => array(
         "name" => "Catalan; Valencian",
+        "flag" => "catalonia",
         "nativeName" => "Català"
     ),
     "ch" => array(
@@ -146,10 +148,12 @@ return array(
     ),
     "cs" => array(
         "name" => "Czech",
+        "flag" => "CZ",
         "nativeName" => "česky, čeština"
     ),
     "da" => array(
         "name" => "Danish",
+        "flag" => "DK",
         "nativeName" => "dansk"
     ),
     "dv" => array(
@@ -202,6 +206,7 @@ return array(
     ),
     "ka" => array(
         "name" => "Georgian",
+        "flag" => "GE",
         "nativeName" => "ქართული"
     ),
     "de" => array(
@@ -210,6 +215,7 @@ return array(
     ),
     "el" => array(
         "name" => "Greek, Modern",
+        "flag" => "GR",
         "nativeName" => "Ελληνικά"
     ),
     "gn" => array(
@@ -229,7 +235,9 @@ return array(
         "nativeName" => "Hausa, هَوُسَ"
     ),
     "he" => array(
+        "direction" => "rtl",
         "name" => "Hebrew (modern)",
+        "flag" => "IL",
         "nativeName" => "עברית"
     ),
     "hz" => array(
@@ -238,6 +246,7 @@ return array(
     ),
     "hi" => array(
         "name" => "Hindi",
+        "flag" => "IN",
         "nativeName" => "हिन्दी, हिंदी"
     ),
     "ho" => array(
@@ -290,6 +299,7 @@ return array(
     ),
     "ja" => array(
         "name" => "Japanese",
+        "flag" => "JP",
         "nativeName" => "日本語 (にほんご/にっぽんご)"
     ),
     "jv" => array(
@@ -501,6 +511,8 @@ return array(
         "nativeName" => "पाऴि"
     ),
     "fa" => array(
+        "direction" => "rtl",
+        "flag" => "IR",
         "name" => "Persian",
         "nativeName" => "فارسی"
     ),
@@ -562,6 +574,7 @@ return array(
     ),
     "sr" => array(
         "name" => "Serbian",
+        "flag" => "RS",
         "nativeName" => "српски језик"
     ),
     "gd" => array(
@@ -582,6 +595,7 @@ return array(
     ),
     "sl" => array(
         "name" => "Slovene",
+        "flag" => "SI",
         "nativeName" => "slovenščina"
     ),
     "so" => array(
@@ -602,6 +616,7 @@ return array(
     ),
     "sw" => array(
         "name" => "Swahili",
+        "flag" => "KE",
         "nativeName" => "Kiswahili"
     ),
     "ss" => array(
@@ -626,7 +641,22 @@ return array(
     ),
     "th" => array(
         "name" => "Thai",
-        "nativeName" => "ไทย"
+        "nativeName" => "ไทย",
+        "fonts" => array(
+            "garuda" => array(
+                'R' => array("Garuda.ttf","http://www.osticket.com/sites/default/files/fonts/Garuda.ttf"),
+                'B' => array("Garuda-Bold.ttf","http://www.osticket.com/sites/default/files/fonts/Garuda-Bold.ttf"),
+                'I' => array("Garuda-Oblique.ttf","http://www.osticket.com/sites/default/files/fonts/Garuda-Oblique.ttf"),
+                'BI' => array("Garuda-BoldOblique.ttf","http://www.osticket.com/sites/default/files/fonts/Garuda-BoldOblique.ttf"),
+                ':sub' => true,
+            ),
+            "norasi" => array(
+                'R' => array("Norasi.ttf","http://www.osticket.com/sites/default/files/fonts/Norasi.ttf"),
+                'B' => array("Norasi-Bold.ttf","http://www.osticket.com/sites/default/files/fonts/Norasi-Bold.ttf"),
+                'I' => array("Norasi-Oblique.ttf","http://www.osticket.com/sites/default/files/fonts/Norasi-Oblique.ttf"),
+                'BI' => array("Norasi-BoldOblique.ttf","http://www.osticket.com/sites/default/files/fonts/Norasi-BoldOblique.ttf"),
+            ),
+		),
     ),
     "ti" => array(
         "name" => "Tigrinya",
@@ -678,6 +708,7 @@ return array(
     ),
     "uk" => array(
         "name" => "Ukrainian",
+        "flag" => "UA",
         "nativeName" => "українська"
     ),
     "ur" => array(
diff --git a/include/mpdf/classes/cssmgr.php b/include/mpdf/classes/cssmgr.php
index c6eff8e9eafdd3d1a377c89e74c90c9252c10c14..aef74542d44b0ab9019f40841162b59464151750 100755
--- a/include/mpdf/classes/cssmgr.php
+++ b/include/mpdf/classes/cssmgr.php
@@ -221,9 +221,8 @@ function ReadCSS($html) {
 		}
 	}
 
-	// mPDF 5.5.13
 	// Replace any background: url(data:image... with temporary image file reference
-	preg_match_all("/(url\(data:image\/(jpeg|gif|png);base64,(.*)\))/si", $CSSstr, $idata);
+	preg_match_all("/(url\(data:image\/(jpeg|gif|png);base64,(.*?)\))/si", $CSSstr, $idata);	// mPDF 5.7.2
 	if (count($idata[0])) { 
 		for($i=0;$i<count($idata[0]);$i++) {
 			$file = _MPDF_TEMP_PATH.'_tempCSSidata'.RAND(1,10000).'_'.$i.'.'.$idata[2][$i];
@@ -699,6 +698,13 @@ function fixCSS($prop) {
 			}
 			else { $newprop[$k] = $v; }
 		}
+		else if ($k == 'LIST-STYLE') {	// mPDF 5.7.2
+			if (preg_match('/(lower-roman|upper-roman|lower-latin|lower-alpha|upper-latin|upper-alpha|none|decimal|disc|circle|square|arabic-indic|bengali|devanagari|gujarati|gurmukhi|kannada|malayalam|oriya|persian|tamil|telugu|thai|urdu|cambodian|khmer|lao)/i',$v,$m)
+			|| preg_match('/U\+([a-fA-F0-9]+)/i',$v,$m)) { 
+				$newprop['LIST-STYLE-TYPE'] = strtolower(trim($m[1]));
+			}
+		}
+
 
 		else { 
 			$newprop[$k] = $v; 
@@ -1241,8 +1247,13 @@ function MergeCSS($inherit,$tag,$attr) {
 	}
 	//===============================================
 /*-- TABLES --*/
+	// mPDF 5.7.3
+	// cellSpacing overwrites TABLE default but not specific CSS set on table
+	if ($tag=='TABLE' && isset($attr['CELLSPACING'])) {
+		$p['BORDER-SPACING-H'] = $p['BORDER-SPACING-V'] = $attr['CELLSPACING'];
+	}
 	// cellPadding overwrites TD/TH default but not specific CSS set on cell
-	if (($tag=='TD' || $tag=='TH') && isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding']) && ($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'] || $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding']===0)) { 
+	if (($tag=='TD' || $tag=='TH') && isset($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding']) && ($this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'] || $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding']==='0')) { 	// mPDF 5.7.3
 		$p['PADDING-LEFT'] = $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'];
 		$p['PADDING-RIGHT'] = $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'];
 		$p['PADDING-TOP'] = $this->mpdf->table[$this->mpdf->tableLevel][$this->mpdf->tbctr[$this->mpdf->tableLevel]]['cell_padding'];
diff --git a/include/mpdf/config.php b/include/mpdf/config.php
index 55c64b648720b27311818dee1b3944a3bf401a6e..fa4fc26e7036b6bec38fe8da4bbdc9c433192d80 100755
--- a/include/mpdf/config.php
+++ b/include/mpdf/config.php
@@ -60,7 +60,7 @@ $this->maxTTFFilesize = 2000;
 // and/or Can set at runtime
 $this->percentSubset = 30;
 
-$this->useAdobeCJK = false;		// Uses Adobe CJK fonts for CJK languages
+$this->useAdobeCJK = true;		// Uses Adobe CJK fonts for CJK languages
 			// default TRUE; only set false if you have defined some available fonts that support CJK
 			// If true this will not stop use of other CJK fonts if specified by font-family:
 			// and vice versa i.e. only dictates behaviour when specified by lang="" incl. AutoFont()
@@ -77,7 +77,7 @@ $this->biDirectional=false;			// automatically determine BIDI text in LTR page
 $this->autoFontGroupSize = 2;			// 1: individual words are spanned; 2: words+; 3: as big chunks as possible.
 $this->useLang = true;				// Default changed in mPDF 4.0
 
-$this->useSubstitutions = false;		// Substitute missing characters in UTF-8(multibyte) documents - from other fonts
+$this->useSubstitutions = true;		// Substitute missing characters in UTF-8(multibyte) documents - from other fonts
 $this->falseBoldWeight = 5;			// Weight for bold text when using an artificial (outline) bold; value 0 (off) - 10 (rec. max)
 
 // CONFIGURATION
@@ -561,10 +561,10 @@ $this->fontsizes = array('XX-SMALL'=>0.7, 'X-SMALL'=>0.77, 'SMALL'=>0.86, 'MEDIU
 $this->allowedCSStags = 'DIV|P|H1|H2|H3|H4|H5|H6|FORM|IMG|A|BODY|TABLE|HR|THEAD|TFOOT|TBODY|TH|TR|TD|UL|OL|LI|PRE|BLOCKQUOTE|ADDRESS|DL|DT|DD';
 $this->allowedCSStags .= '|ARTICLE|ASIDE|FIGURE|FIGCAPTION|FOOTER|HEADER|HGROUP|NAV|SECTION|MARK|DETAILS|SUMMARY|METER|PROGRESS|TIME'; // mPDF 5.5.09
 $this->allowedCSStags .= '|SPAN|TT|I|B|BIG|SMALL|EM|STRONG|DFN|CODE|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|STRIKE|S|U|DEL|INS|Q|FONT';
-$this->allowedCSStags .= '|SELECT|INPUT|TEXTAREA|CAPTION|FIELDSET|LEGEND';	// mPDF 5.4.18
-$this->allowedCSStags .= '|TEXTCIRCLE|DOTTAB';	// mPDF 5.5.23	// mPDF 5.6.33
+$this->allowedCSStags .= '|SELECT|INPUT|TEXTAREA|CAPTION|FIELDSET|LEGEND';
+$this->allowedCSStags .= '|TEXTCIRCLE|DOTTAB|MAIN';	// mPDF 5.7.3
 
-$this->outerblocktags = array('DIV','FORM','CENTER','DL','FIELDSET','ARTICLE','ASIDE','FIGURE','FIGCAPTION', 'FOOTER','HEADER','HGROUP','NAV','SECTION','DETAILS','SUMMARY');	// mPDF 5.5.09 // mPDF 5.5.22
+$this->outerblocktags = array('DIV','FORM','CENTER','DL','FIELDSET','ARTICLE','ASIDE','FIGURE','FIGCAPTION', 'FOOTER','HEADER','HGROUP','MAIN','NAV','SECTION','DETAILS','SUMMARY');	// mPDF 5.7.3
 $this->innerblocktags = array('P','BLOCKQUOTE','ADDRESS','PRE','H1','H2','H3','H4','H5','H6','DT','DD','CAPTION');
 
 
diff --git a/include/mpdf/config_fonts.php b/include/mpdf/config_fonts.php
index ab6c8a7247299b92484904e755c0ce1635e2987f..31c4e3015c289a003309130f9731ec82d52bf257 100755
--- a/include/mpdf/config_fonts.php
+++ b/include/mpdf/config_fonts.php
@@ -129,7 +129,7 @@ $this->fontdata = array(
 		'R' => "ocrb10.ttf",
 		),
 
-/* Thai fonts */
+/* Thai fonts
 	"garuda" => array(
 		'R' => "Garuda.ttf",
 		'B' => "Garuda-Bold.ttf",
@@ -301,4 +301,12 @@ $this->mono_fonts = array('dejavusansmono','freemono','liberationmono','courier'
 				'couriernew','monotypecorsiva'
 );
 
-?>
\ No newline at end of file
+// Add fonts from language packs
+
+list($phar_fonts, $phar_subs) = Internationalization::getTtfFonts();
+$this->fontdata += $phar_fonts;
+foreach ($phar_subs as $simple) {
+    if (!in_array($simple, $this->backupSubsFont))
+        $this->backupSubsFont[] = $simple;
+}
+?>
diff --git a/include/mpdf/includes/functions.php b/include/mpdf/includes/functions.php
index 91e75da4a8ef1ad076f9c56055d22009e62ad275..ce88e6f476d467a77548094dd35d01762c0d90ec 100755
--- a/include/mpdf/includes/functions.php
+++ b/include/mpdf/includes/functions.php
@@ -1,5 +1,13 @@
 <?php
 
+// mPDF 5.7
+// Replace a section of an array with the elements in reverse
+function array_splice_reverse(&$arr, $offset, $length) {
+	$tmp = (array_reverse(array_slice($arr, $offset, $length)));
+	array_splice($arr, $offset, $length, $tmp);
+}
+
+
 // mPDF 5.6.23
 function array_insert(&$array, $value, $offset) {
 	if (is_array($array)) {
@@ -92,12 +100,30 @@ function PreparePreText($text,$ff='//FF//') {
 if(!function_exists('strcode2utf')){ 
   function strcode2utf($str,$lo=true) {
 	//converts all the &#nnn; and &#xhhh; in a string to Unicode
-	if ($lo) { $lo = 1; } else { $lo = 0; }
-	$str = preg_replace('/\&\#([0-9]+)\;/me', "code2utf('\\1',{$lo})",$str);
-	$str = preg_replace('/\&\#x([0-9a-fA-F]+)\;/me', "codeHex2utf('\\1',{$lo})",$str);
+	// mPDF 5.7
+	if ($lo) {
+		$str = preg_replace_callback('/\&\#([0-9]+)\;/m', 'code2utf_lo_callback', $str);
+		$str = preg_replace_callback('/\&\#x([0-9a-fA-F]+)\;/m', 'codeHex2utf_lo_callback', $str);
+	}
+	else {
+		$str = preg_replace_callback('/\&\#([0-9]+)\;/m', 'code2utf_callback', $str);
+		$str = preg_replace_callback('/\&\#x([0-9a-fA-F]+)\;/m', 'codeHex2utf_callback', $str);
+	}
 	return $str;
   }
 }
+function code2utf_callback($matches) {
+	return code2utf($matches[1], 0);
+}
+function code2utf_lo_callback($matches) {
+	return code2utf($matches[1], 1);
+}
+function codeHex2utf_callback($matches) {
+	return codeHex2utf($matches[1], 0);
+}
+function codeHex2utf_lo_callback($matches) {
+	return codeHex2utf($matches[1], 1);
+}
 
 if(!function_exists('code2utf')){ 
   function code2utf($num,$lo=true){
diff --git a/include/mpdf/mpdf.php b/include/mpdf/mpdf.php
index 91d310dda8dc9c810d5697873de236a2f5a7b3a1..af04fa3b483c53e838bd8f4835d2443dbe073764 100755
--- a/include/mpdf/mpdf.php
+++ b/include/mpdf/mpdf.php
@@ -2,10 +2,10 @@
 
 // ******************************************************************************
 // Software: mPDF, Unicode-HTML Free PDF generator                              *
-// Version:  5.7     based on                                                   *
+// Version:  5.7.3     based on                                                   *
 //           FPDF by Olivier PLATHEY                                            *
 //           HTML2FPDF by Renato Coelho                                         *
-// Date:     2013-07-14                                                         *
+// Date:     2013-09-01                                                         *
 // Author:   Ian Back <ianb@bpm1.com>                                           *
 // License:  GPL                                                                *
 //                                                                              *
@@ -13,7 +13,7 @@
 // ******************************************************************************
 
 
-define('mPDF_VERSION','5.7');
+define('mPDF_VERSION','5.7.3');
 
 //Scale factor
 define('_MPDFK', (72/25.4));
@@ -269,6 +269,7 @@ var $directw;
 //////////////////////
 // INTERNAL VARIABLES
 //////////////////////
+var $uniqstr;	// mPDF 5.7.2
 var $writingToC;	// mPDF 5.6.38
 // mPDF 5.6.01
 var $layers;
@@ -336,6 +337,10 @@ var $pdf_version;
 var $noImageFile;
 var $lastblockbottommargin;
 var $baselineC;
+// mPDF 5.7.3  inline text-decoration parameters
+var $baselineSup;
+var $baselineSub;
+var $baselineS;
 var $subPos;
 var $subArrMB;
 var $ReqFontStyle;
@@ -613,7 +618,6 @@ var $divheight;
 var $divrevert;
 var $spanbgcolor;
 
-var $spanlvl;
 var $listlvl;
 var $listnum;
 var $listtype;
@@ -850,6 +854,7 @@ function mPDF($mode='',$format='A4',$default_font_size=0,$default_font='',$mgl=1
 	$this->useSubstitutionsMB =& $this->useSubstitutions;
 
 	$this->writingToC = false;	// mPDF 5.6.38
+	$this->uniqstr = '20110230';	// mPDF 5.7.2
 	// mPDF 5.6.01
 	$this->layers = array();
 	$this->current_layer = 0;
@@ -977,6 +982,11 @@ function mPDF($mode='',$format='A4',$default_font_size=0,$default_font='',$mgl=1
 
 
 	$this->baselineC = 0.35;	// Baseline for text
+	// mPDF 5.7.3  inline text-decoration parameters
+	$this->baselineSup = 0.5;	// Sets default change in baseline for <sup> text bas factor of preceeding fontsize
+	$this->baselineSub = -0.2;	// Sets default change in baseline for <sub> text bas factor of preceeding fontsize
+	$this->baselineS = 0.3;		// Sets default height for <strike> text as factor of fontsize
+
 	$this->noImageFile = str_replace("\\","/",dirname(__FILE__)) . '/includes/no_image.jpg';
 	$this->subPos = 0;
 	$this->forceExactLineheight = false;
@@ -1146,7 +1156,7 @@ function mPDF($mode='',$format='A4',$default_font_size=0,$default_font='',$mgl=1
 	$optcore = false;
 	$onlyCoreFonts = false;
 	if (preg_match('/([\-+])aCJK/i',$mode, $m)) {
-		preg_replace('/([\-+])aCJK/i','',$mode);
+		$mode = preg_replace('/([\-+])aCJK/i','',$mode);
 		if ($m[1]=='+') { $this->useAdobeCJK = true; }
 		else { $this->useAdobeCJK = false; }
 	}
@@ -1210,7 +1220,9 @@ function mPDF($mode='',$format='A4',$default_font_size=0,$default_font='',$mgl=1
 		$this->useSubstitutions = true;
 		$this->SetSubstitutions();
 	}
-	else { $this->useSubstitutions = false; }
+    // osTicket custom — seems that substitions are necessary for eastern
+    // languages to work with packaged fonts
+	//else { $this->useSubstitutions = false; }
 
 /*-- HTML-CSS --*/
 
@@ -1682,8 +1694,8 @@ function SetAlpha($alpha, $bm='Normal', $return=false, $mode='B') {
 		$alpha = 1;
 	}
 	$a = array('BM'=>'/'.$bm);
-	if ($mode=='F' || $mode='B') $a['ca'] = $alpha;
-	if ($mode=='S' || $mode='B') $a['CA'] = $alpha;
+	if ($mode=='F' || $mode=='B') $a['ca'] = $alpha;	// mPDF 5.7.2
+	if ($mode=='S' || $mode=='B') $a['CA'] = $alpha;	// mPDF 5.7.2
 	$gs = $this->AddExtGState($a);
 	if ($return) { return sprintf('/GS%d gs', $gs); }
 	else { $this->_out(sprintf('/GS%d gs', $gs)); }
@@ -1765,7 +1777,7 @@ function Close() {
 	$s .= $this->PrintBodyBackgrounds();
 
 	$s .= $this->PrintPageBackgrounds();
-	$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.date('jY').')/', "\n".$s."\n".'\\1', $this->pages[$this->page]);
+	$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.$this->uniqstr.')/', "\n".$s."\n".'\\1', $this->pages[$this->page]);
 	$this->pageBackgrounds = array();
 
 	if($this->visibility!='visible')
@@ -2246,9 +2258,124 @@ function PrintTableBackgrounds($adjustmenty=0) {
 			$w = $pb['w']*_MPDFK;
 			$h = -$pb['h']*_MPDFK;
 			if (isset($pb['clippath']) && $pb['clippath']) { $s .= $pb['clippath']."\n"; }
-			if ($pb['opacity']>0 && $pb['opacity']<1) { $opac = $this->SetAlpha($pb['opacity'],'Normal',true); }
-			else { $opac = ''; }
-			$s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $x, $y, $w, $h) ."\n";
+
+			// mPDF 5.7.3
+			if (($this->writingHTMLfooter || $this->writingHTMLheader) && (!isset($pb['clippath']) || $pb['clippath']=='') ) {
+				// Set clipping path
+				$pb['clippath'] = sprintf(' q 0 w %.3F %.3F m %.3F %.3F l %.3F %.3F l %.3F %.3F l %.3F %.3F l W n ', $x, $y, $x, $y+$h, $x+$w, $y+$h, $x+$w, $y, $x, $y);
+			}
+
+			if (isset($pb['clippath']) && $pb['clippath']) { $s .= $pb['clippath']."\n"; }
+
+			// mPDF 5.7.3
+			if ($this->writingHTMLfooter || $this->writingHTMLheader) {	// Write each (tiles) image rather than use as a pattern
+				$iw = $pb['orig_w']/_MPDFK;
+				$ih = $pb['orig_h']/_MPDFK;
+
+				$w = $pb['w'];
+				$h = $pb['h'];
+				$x0 = $pb['x'];
+				$y0 = $pb['y'];
+
+				if (isset($pb['bpa']) && $pb['bpa']) {
+					$w = $pb['bpa']['w'];
+					$h = $pb['bpa']['h'];
+					$x0 = $pb['bpa']['x'];
+					$y0 = $pb['bpa']['y'];
+				}
+
+				if (isset($pb['size']['w']) && $pb['size']['w']) {
+					$size = $pb['size'];
+
+					if ($size['w']=='contain') {
+					// Scale the image, while preserving its intrinsic aspect ratio (if any), to the largest size such that both its width and its height can fit inside the background positioning area.
+					// Same as resize==3
+						$ih = $ih * $pb['bpa']['w']/$iw;
+						$iw = $pb['bpa']['w'];
+						if ($ih > $pb['bpa']['h']) {
+							$iw = $iw * $pb['bpa']['h']/$ih;
+							$ih = $pb['bpa']['h'];
+						}
+					}
+					else if ($size['w']=='cover') {
+					// Scale the image, while preserving its intrinsic aspect ratio (if any), to the smallest size such that both its width and its height can completely cover the background positioning area.
+						$ih = $ih * $pb['bpa']['w']/$iw;
+						$iw = $pb['bpa']['w'];
+						if ($ih < $pb['bpa']['h']) {
+							$iw = $iw * $ih/$pb['bpa']['h'];
+							$ih = $pb['bpa']['h'];
+						}
+					}
+					else {
+						if (stristr($size['w'] ,'%')) {
+							$size['w'] += 0;
+							$size['w'] /= 100;
+							$size['w'] = ($pb['bpa']['w'] * $size['w']);
+						}
+						if (stristr($size['h'] ,'%')) {
+							$size['h'] += 0;
+							$size['h'] /= 100;
+							$size['h'] = ($pb['bpa']['h'] * $size['h']);
+						}
+						if ($size['w']=='auto' && $size['h']=='auto') {
+							$iw = $iw;
+							$ih = $ih;
+						}
+						else if ($size['w']=='auto' && $size['h']!='auto') {
+							$iw = $iw * $size['h']/$ih;
+							$ih = $size['h'];
+						}
+						else if ($size['w']!='auto' && $size['h']=='auto') {
+							$ih = $ih * $size['w']/$iw;
+							$iw = $size['w'];
+						}
+						else {
+							$iw = $size['w'];
+							$ih = $size['h'];
+						}
+					}
+				}
+
+				// Number to repeat
+				if ($pb['x_repeat']) { $nx = ceil($pb['w']/$iw)+1; }
+				else { $nx = 1; }
+				if ($pb['y_repeat']) { $ny = ceil($pb['h']/$ih)+1; }
+				else { $ny = 1; }
+
+				$x_pos = $pb['x_pos'];
+				if (stristr($x_pos ,'%') ) {
+					$x_pos += 0;
+					$x_pos /= 100;
+					$x_pos = ($pb['bpa']['w'] * $x_pos) - ($iw * $x_pos);
+				}
+				$y_pos = $pb['y_pos'];
+				if (stristr($y_pos ,'%') ) {
+					$y_pos += 0;
+					$y_pos /= 100;
+					$y_pos = ($pb['bpa']['h'] * $y_pos) - ($ih * $y_pos);
+				}
+				if ($nx>1) {
+					while($x_pos>($pb['x']-$pb['bpa']['x'])) { $x_pos -= $iw; }
+				}
+				if ($ny>1) {
+					while($y_pos>($pb['y']-$pb['bpa']['y'])) { $y_pos -= $ih; }
+				}
+				for($xi=0;$xi<$nx;$xi++) {
+				  for($yi=0;$yi<$ny;$yi++) {
+					$x = $x0 + $x_pos + ($iw*$xi);
+					$y = $y0 + $y_pos + ($ih*$yi);
+					if ($pb['opacity']>0 && $pb['opacity']<1) { $opac = $this->SetAlpha($pb['opacity'],'Normal',true); }
+					else { $opac = ''; }
+					$s .= sprintf("q %s %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q", $opac,$iw*_MPDFK,$ih*_MPDFK,$x*_MPDFK,($this->h-($y+$ih))*_MPDFK,$pb['image_id']) ."\n";
+				  }
+				}
+			}
+			else {
+				if (($pb['opacity']>0 || $pb['opacity']==='0') && $pb['opacity']<1) { $opac = $this->SetAlpha($pb['opacity'],'Normal',true); }
+				else { $opac = ''; }
+				$s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $x, $y, $w, $h) ."\n";
+			}
+
 			if (isset($pb['clippath']) && $pb['clippath']) { $s .= 'Q'."\n"; }
 		  }
 		}
@@ -2344,7 +2471,7 @@ function AddPage($orientation='',$condition='', $resetpagenum='', $pagenumstyle=
 		$s = $this->PrintPageBackgrounds();
 
 		// Writes after the marker so not overwritten later by page background etc.
-		$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.date('jY').')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
+		$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.$this->uniqstr.')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
 		$this->pageBackgrounds = array();
 		$family=$this->FontFamily;
 		$style=$this->FontStyle.($this->U ? 'U' : '').($this->S ? 'S' : '');
@@ -2485,7 +2612,7 @@ function AddPage($orientation='',$condition='', $resetpagenum='', $pagenumstyle=
 		$s .= $this->PrintBodyBackgrounds();
 
 		$s .= $this->PrintPageBackgrounds();
-		$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.date('jY').')/', "\n".$s."\n".'\\1', $this->pages[$this->page]);
+		$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.$this->uniqstr.')/', "\n".$s."\n".'\\1', $this->pages[$this->page]);
 		$this->pageBackgrounds = array();
 	}
 
@@ -2545,9 +2672,9 @@ function AddPage($orientation='',$condition='', $resetpagenum='', $pagenumstyle=
 	}
 
 	// Tiling Patterns
-	$this->_out('___PAGE___START'.date('jY'));
-	$this->_out('___BACKGROUND___PATTERNS'.date('jY'));
-	$this->_out('___HEADER___MARKER'.date('jY'));
+	$this->_out('___PAGE___START'.$this->uniqstr);
+	$this->_out('___BACKGROUND___PATTERNS'.$this->uniqstr);
+	$this->_out('___HEADER___MARKER'.$this->uniqstr);
 	$this->pageBackgrounds = array();
 
 	//Set line cap style to square
@@ -3023,7 +3150,10 @@ function AddFont($family,$style='') {
 		if (!file_exists($ttffile)) { $ttffile = ''; }
 	}
 	if (!$ttffile) {
-		$ttffile = _MPDF_TTFONTPATH.$this->fontdata[$family][$stylekey];
+		$ttffile = $this->fontdata[$family][$stylekey];
+        if ($ttffile[0] != '/' && $ttffile[1] != ':' && strpos($ttffile, 'phar://') !== 0)
+            // Not an absolute path
+            $ttffile = _MPDF_TTFONTPATH.$ttffile;
 		if (!file_exists($ttffile)) { die("mPDF Error - cannot find TTF TrueType font file - ".$ttffile); }
 	}
 	$ttfstat = stat($ttffile);
@@ -3628,9 +3758,8 @@ function Cell($w,$h=0,$txt='',$border=0,$ln=0,$align='',$fill=0,$link='', $curre
 	$oldcolumn = $this->CurrCol;
 	// Automatic page break
 	// Allows PAGE-BREAK-AFTER = avoid to work
-
 	if (!$this->tableLevel && (($this->y+$this->divheight>$this->PageBreakTrigger) || ($this->y+$h>$this->PageBreakTrigger) ||
-		($this->y+($h*2)>$this->PageBreakTrigger && $this->blk[$this->blklvl]['page_break_after_avoid'])) and !$this->InFooter and $this->AcceptPageBreak()) {
+		($this->y+($h*2)+$this->blk[$this->blklvl]['padding_bottom']+$this->blk[$this->blklvl]['margin_bottom']>$this->PageBreakTrigger && $this->blk[$this->blklvl]['page_break_after_avoid'])) and !$this->InFooter and $this->AcceptPageBreak()) {	// mPDF 5.7.2
 		$x=$this->x;//Current X position
 
 
@@ -3694,8 +3823,9 @@ function Cell($w,$h=0,$txt='',$border=0,$ln=0,$align='',$fill=0,$link='', $curre
     		//Calculate baseline Superscript and Subscript Y coordinate adjustment
 		$bfx = $this->baselineC;
     		$baseline = $bfx*$bfs;
-		if($this->SUP) { $baseline += ($bfx-1.05)*$this->FontSize; }
-		else if($this->SUB) { $baseline += ($bfx + 0.04)*$this->FontSize; }
+		// mPDF 5.7.3  inline text-decoration parameters
+		if($this->SUP) { $baseline -= $this->textparam['text-baseline']; }	// mPDF 5.7.1
+		else if($this->SUB) { $baseline -= $this->textparam['text-baseline']; }	// mPDF 5.7.1
 		else if($this->bullet) { $baseline += ($bfx-0.7)*$this->FontSize; }
 
 		// Vertical align (for Images)
@@ -3962,34 +4092,50 @@ function Cell($w,$h=0,$txt='',$border=0,$ln=0,$align='',$fill=0,$link='', $curre
 		  }
 		}
 		// UNDERLINE
-		if($this->U) {
+		if($this->U) {	// mPDF 6
 			$c = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
+			// mPDF 5.7.3  inline text-decoration parameters
+			if (isset($this->textparam['u-decoration']['color'])) {
+				$c = $this->textparam['u-decoration']['color'];
+			}
 			if($this->FillColor!=$c) { $sub .= ' '.$c.' '; }
-			if (isset($this->CurrentFont['up'])) { $up=$this->CurrentFont['up']; }
+			// mPDF 5.7.3  inline text-decoration parameters
+			$decorationfontkey = $this->textparam['decoration-fontkey'];
+			$decorationfontsize = $this->textparam['decoration-fontsize'];
+			if (isset($this->fonts[$decorationfontkey]['up'])) { $up=$this->fonts[$decorationfontkey]['up']; }
 			else { $up = -100; }
-			$adjusty = (-$up/1000* $this->FontSize);
- 			if (isset($this->CurrentFont['ut'])) { $ut=$this->CurrentFont['ut']/1000* $this->FontSize; }
-			else { $ut = 60/1000* $this->FontSize; }
+			$adjusty = (-$up/1000* $decorationfontsize);
+			if (isset($this->fonts[$decorationfontkey]['ut'])) { $ut=$this->fonts[$decorationfontkey]['ut']/1000* $decorationfontsize; }
+			else { $ut = 60/1000* $decorationfontsize; }
+			$ubaseline = $this->baselineC*$bfs - $this->textparam['decoration-baseline'];
 			$olw = $this->LineWidth;
 			$sub .=' '.(sprintf(' %.3F w 0 j 0 J ',$ut*_MPDFK));
-			$sub .=' '.$this->_dounderline($this->x+$dx,$this->y+$baseline+$va+$adjusty,$txt);
+			$sub .=' '.$this->_dounderline($this->x+$dx,$this->y+$ubaseline+$va+$adjusty,$txt,$OTLdata,$textvar);
 			$sub .=' '.(sprintf(' %.3F w 2 j 2 J ',$olw*_MPDFK));
 			if($this->FillColor!=$c) { $sub .= ' '.$this->FillColor.' '; }
 		}
 
    		// STRIKETHROUGH
-		if($this->strike) {
+		if($this->strike) {	// mPDF 6
 			$c = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
+			// mPDF 5.7.3  inline text-decoration parameters
+			if (isset($this->textparam['s-decoration']['color'])) {
+				$c = $this->textparam['s-decoration']['color'];
+			}
 			if($this->FillColor!=$c) { $sub .= ' '.$c.' '; }
-    			//Superscript and Subscript Y coordinate adjustment (now for striked-through texts)
-			if (isset($this->CurrentFont['desc']['CapHeight'])) { $ch=$this->CurrentFont['desc']['CapHeight']; }
+			// mPDF 5.7.3  inline text-decoration parameters
+			$decorationfontkey = $this->textparam['decoration-fontkey'];
+			$decorationfontsize = $this->textparam['decoration-fontsize'];
+ 			//Superscript and Subscript Y coordinate adjustment (now for striked-through texts)
+			if (isset($this->fonts[$decorationfontkey]['desc']['CapHeight'])) { $ch=$this->fonts[$decorationfontkey]['desc']['CapHeight']; }
 			else { $ch = 700; }
-			$adjusty = (-$ch/1000* $this->FontSize) * 0.35;
- 			if (isset($this->CurrentFont['ut'])) { $ut=$this->CurrentFont['ut']/1000* $this->FontSize; }
-			else { $ut = 60/1000* $this->FontSize; }
+			$adjusty = (-$ch/1000* $decorationfontsize) * $this->baselineS;
+ 			if (isset($this->fonts[$decorationfontkey]['ut'])) { $ut=$this->fonts[$decorationfontkey]['ut']/1000* $decorationfontsize; }
+			else { $ut = 60/1000* $decorationfontsize; }
+			$sbaseline = $this->baselineC*$bfs - $this->textparam['decoration-baseline'];
 			$olw = $this->LineWidth;
 			$sub .=' '.(sprintf(' %.3F w 0 j 0 J ',$ut*_MPDFK));
-			$sub .=' '.$this->_dounderline($this->x+$dx,$this->y+$baseline+$va+$adjusty,$txt);
+			$sub .=' '.$this->_dounderline($this->x+$dx,$this->y+$sbaseline+$va+$adjusty,$txt,$OTLdata,$textvar);
 			$sub .=' '.(sprintf(' %.3F w 2 j 2 J ',$olw*_MPDFK));
 			if($this->FillColor!=$c) { $sub .= ' '.$this->FillColor.' '; }
 		}
@@ -4893,6 +5039,19 @@ function finishFlowingBlock($endofblock=false, $next='') {
 				$mba = max($mba, $oh);
 			}
 		  }
+		  // mPDF 5.7.3  inline text-decoration parameters
+		  else if (!$is_table && isset($font[$k]['textparam']['text-baseline'])) {
+			if ($font[$k]['textparam']['text-baseline'] > 0) { 	// superscript
+				$nh = ($maxfontsize * $this->baselineC) + $font[$k]['textparam']['text-baseline'] + ($font[$k]['size'] * (1-$this->baselineC));
+				if ($lhfixed && $nh > $def_fontsize) { $this->forceExactLineheight = false; }
+				$af = max($af, ($nh-$maxfontsize));
+			}
+			else if ($font[$k]['textparam']['text-baseline'] < 0) {	// subscript
+				$nh = ($maxfontsize * (1-$this->baselineC)) - $font[$k]['textparam']['text-baseline'] + ($font[$k]['size'] * $this->baselineC);
+				if ($lhfixed && $nh > $def_fontsize) { $this->forceExactLineheight = false; }
+				$bf = max($bf, ($nh-$maxfontsize));
+			}
+		  }
 		}
 		if ((!$lhfixed || !$this->forceExactLineheight) && ($af > (($maxlineHeight - $maxfontsize)/2) || $bf > (($maxlineHeight - $maxfontsize)/2))) {
 			$maxlineHeight = $maxfontsize + $af + $bf;
@@ -5446,7 +5605,10 @@ function printobjectbuffer($is_table=false, $blockdir=false) {
 		   }
 		// IMAGE
 		   if ($objattr['type'] == 'image') {
-			// mPDF 5.6.01  - LAYERS
+			// mPDF 5.7.3 TRANSFORMS
+			if (isset($objattr['transform'])) {
+				$this->_out("\n".'% BTR');	// Begin Transform
+			}
 			if (isset($objattr['z-index']) && $objattr['z-index'] > 0 && $this->currentlayer==0) {
 				$this->BeginLayer($objattr['z-index']);
 			}
@@ -5491,6 +5653,78 @@ function printobjectbuffer($is_table=false, $blockdir=false) {
 			if ($tr) { $tr .= ' '; }
 			$gradmask = '';
 
+			// mPDF 5.7.3 TRANSFORMS
+			$tr2 = '';
+			if (isset($objattr['transform'])) {
+				$maxsize_x = $w;
+				$maxsize_y = $h;
+				$cx = $x + $w/2;
+				$cy = $y + $h/2;
+				preg_match_all('/(translatex|translatey|translate|scalex|scaley|scale|rotate|skewX|skewY|skew)\((.*?)\)/is',$objattr['transform'],$m);
+				if (count($m[0])) {
+					for($i=0; $i<count($m[0]); $i++) {
+						$c = strtolower($m[1][$i]);
+						$v = trim($m[2][$i]);
+						$vv = preg_split('/[ ,]+/',$v);
+						if ($c=='translate' && count($vv)) {
+							$translate_x = $this->ConvertSize($vv[0],$maxsize_x,false,false);
+							if (count($vv)==2) { $translate_y = $this->ConvertSize($vv[1],$maxsize_y,false,false); }
+							else { $translate_y = 0; }
+							$tr2 .= $this->transformTranslate($translate_x, $translate_y, true).' ';
+						}
+						else if ($c=='translatex' && count($vv)) {
+							$translate_x = $this->ConvertSize($vv[0],$maxsize_x,false,false);
+							$tr2 .= $this->transformTranslate($translate_x, 0, true).' ';
+						}
+						else if ($c=='translatey' && count($vv)) {
+							$translate_y = $this->ConvertSize($vv[1],$maxsize_y,false,false);
+							$tr2 .= $this->transformTranslate(0, $translate_y, true).' ';
+						}
+						else if ($c=='scale' && count($vv)) {
+							$scale_x = $vv[0] * 100;
+							if (count($vv)==2) { $scale_y = $vv[1] * 100; }
+							else { $scale_y = $scale_x; }
+							$tr2 .= $this->transformScale($scale_x, $scale_y, $cx, $cy, true).' ';
+						}
+						else if ($c=='scalex' && count($vv)) {
+							$scale_x = $vv[0] * 100;
+							$tr2 .= $this->transformScale($scale_x, 0, $cx, $cy, true).' ';
+						}
+						else if ($c=='scaley' && count($vv)) {
+							$scale_y = $vv[1] * 100;
+							$tr2 .= $this->transformScale(0, $scale_y, $cx, $cy, true).' ';
+						}
+						else if ($c=='skew' && count($vv)) {
+							$angle_x = $this->ConvertAngle($vv[0], false);
+							if (count($vv)==2) { $angle_y = $this->ConvertAngle($vv[1], false); }
+							else { $angle_y = 0; }
+							$tr2 .= $this->transformSkew($angle_x, $angle_y, $cx, $cy, true).' ';
+						}
+						else if ($c=='skewx' && count($vv)) {
+							$angle = $this->ConvertAngle($vv[0], false);
+							$tr2 .= $this->transformSkew($angle, 0, $cx, $cy, true).' ';
+						}
+						else if ($c=='skewy' && count($vv)) {
+							$angle = $this->ConvertAngle($vv[0], false);
+							$tr2 .= $this->transformSkew(0, $angle, $cx, $cy, true).' ';
+						}
+						else if ($c=='rotate' && count($vv)) {
+							$angle = $this->ConvertAngle($vv[0]);
+							$tr2 .= $this->transformRotate($angle, $cx, $cy, true).' ';
+						}
+					}
+				}
+			}
+			// mPDF 5.7.3 TRANSFORMS / BACKGROUND COLOR
+			// Transform also affects image background
+			if ($tr2) { $this->_out('q '.$tr2.' '); }
+			if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {
+				$bgcol = $objattr['bgcolor'];
+				$this->SetFColor($bgcol);
+	 			$this->Rect($x,$y,$w,$h, 'F');
+				$this->SetFColor($this->ConvertColor(255));
+			}
+			if ($tr2) { $this->_out('Q'); }
 
 /*-- BACKGROUNDS --*/
 			if (isset($objattr['GRADIENT-MASK'])) {
@@ -5504,29 +5738,37 @@ function printobjectbuffer($is_table=false, $blockdir=false) {
 /*-- END BACKGROUNDS --*/
 /*-- IMAGES-WMF --*/
 			if (isset($objattr['itype']) && $objattr['itype']=='wmf') {
-				$outstring = sprintf('q '.$tr.'%.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, -$sy, $objattr['INNER-X']*_MPDFK-$sx*$objattr['wmf_x'], (($this->h-$objattr['INNER-Y'])*_MPDFK)+$sy*$objattr['wmf_y'], $objattr['ID']);
+				$outstring = sprintf('q '.$tr.$tr2.'%.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, -$sy, $objattr['INNER-X']*_MPDFK-$sx*$objattr['wmf_x'], (($this->h-$objattr['INNER-Y'])*_MPDFK)+$sy*$objattr['wmf_y'], $objattr['ID']);	// mPDF 5.7.3 TRANSFORMS
 			}
 			else
 /*-- END IMAGES-WMF --*/
 			if (isset($objattr['itype']) && $objattr['itype']=='svg') {
-				$outstring = sprintf('q '.$tr.'%.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, -$sy, $objattr['INNER-X']*_MPDFK-$sx*$objattr['wmf_x'], (($this->h-$objattr['INNER-Y'])*_MPDFK)+$sy*$objattr['wmf_y'], $objattr['ID']);
+				$outstring = sprintf('q '.$tr.$tr2.'%.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, -$sy, $objattr['INNER-X']*_MPDFK-$sx*$objattr['wmf_x'], (($this->h-$objattr['INNER-Y'])*_MPDFK)+$sy*$objattr['wmf_y'], $objattr['ID']);	// mPDF 5.7.3 TRANSFORMS
 			}
 			else {
-				$outstring = sprintf("q ".$tr."%.3F 0 0 %.3F %.3F %.3F cm ".$gradmask."/I%d Do Q",$obiw*_MPDFK, $obih*_MPDFK, $objattr['INNER-X'] *_MPDFK, ($this->h-($objattr['INNER-Y'] +$obih ))*_MPDFK,$objattr['ID'] );
+				$outstring = sprintf("q ".$tr.$tr2."%.3F 0 0 %.3F %.3F %.3F cm ".$gradmask."/I%d Do Q",$obiw*_MPDFK, $obih*_MPDFK, $objattr['INNER-X'] *_MPDFK, ($this->h-($objattr['INNER-Y'] +$obih ))*_MPDFK,$objattr['ID'] );	// mPDF 5.7.3 TRANSFORMS
 			}
 			$this->_out($outstring);
 			// LINK
 			if (isset($objattr['link'])) $this->Link($objattr['INNER-X'],$objattr['INNER-Y'],$objattr['INNER-WIDTH'],$objattr['INNER-HEIGHT'],$objattr['link']);
 			if (isset($objattr['opacity'])) { $this->SetAlpha(1); }
+
+			// mPDF 5.7.3 TRANSFORMS
+			// Transform also affects image borders
+			if ($tr2) { $this->_out('q '.$tr2.' '); }
 			if ((isset($objattr['border_top']) && $objattr['border_top']>0) || (isset($objattr['border_left']) && $objattr['border_left']>0) || (isset($objattr['border_right']) && $objattr['border_right']>0) || (isset($objattr['border_bottom']) && $objattr['border_bottom']>0)) { $this->PaintImgBorder($objattr,$is_table); }
+			if ($tr2) { $this->_out('Q'); }
+
 			if(isset($objattr['visibility']) && $objattr['visibility']!='visible' && $objattr['visibility']) {
 				$this->SetVisibility('visible');
 			}
-			// mPDF 5.6.01  - LAYERS
 			if (isset($objattr['z-index']) && $objattr['z-index'] > 0 && $this->currentlayer==0) {
 				$this->EndLayer();
 			}
-
+			// mPDF 5.7.3 TRANSFORMS
+			if (isset($objattr['transform'])) {
+				$this->_out("\n".'% ETR');	// Begin Transform
+			}
 		   }
 
 /*-- BARCODES --*/
@@ -6086,6 +6328,17 @@ function WriteFlowingBlock( $s)
 			$savedContent = array_pop( $content );
 			$savedContentB = array_pop($contentB);	// mPDF 5.6.20
 			$savedFont = array_pop( $font );
+
+			// mPDF 5.7.2
+			// e.g: |first chunk |second chunk |[IndexEntry]|doesntfit
+			if (isset($this->objectbuffer[(count($content)-1)]) && $this->objectbuffer[(count($content)-1)]['OUTER-WIDTH'] < 0.001) {
+				$savedObj = $this->objectbuffer[(count($content)-1)];
+				array_pop( $content );
+				array_pop( $contentB );
+				array_pop( $font );
+				array_pop( $this->objectbuffer );
+			}
+
 			// trim any trailing spaces off the last bit of content
 			$currContent =& $content[ count( $content ) - 1 ];
 			$currContent = rtrim( $currContent );
@@ -6243,6 +6496,19 @@ function WriteFlowingBlock( $s)
 				$mba = max($mba, $oh);
 			}
 		  }
+		  // mPDF 5.7.3  inline text-decoration parameters
+		  else if (!$is_table && isset($font[$k]['textparam']['text-baseline'])) {
+			if ($font[$k]['textparam']['text-baseline'] > 0) { 	// superscript
+				$nh = ($maxfontsize * $this->baselineC) + $font[$k]['textparam']['text-baseline'] + ($font[$k]['size'] * (1-$this->baselineC));
+				if ($lhfixed && $nh > $def_fontsize) { $this->forceExactLineheight = false; }
+				$af = max($af, ($nh-$maxfontsize));
+			}
+			else if ($font[$k]['textparam']['text-baseline'] < 0) {	// subscript
+				$nh = ($maxfontsize * (1-$this->baselineC)) - $font[$k]['textparam']['text-baseline'] + ($font[$k]['size'] * $this->baselineC);
+				if ($lhfixed && $nh > $def_fontsize) { $this->forceExactLineheight = false; }
+				$bf = max($bf, ($nh-$maxfontsize));
+			}
+		  }
 		}
 		if ((!$lhfixed || !$this->forceExactLineheight) && ($af > (($maxlineHeight - $maxfontsize)/2) || $bf > (($maxlineHeight - $maxfontsize)/2))) {
 			$maxlineHeight = $maxfontsize + $af + $bf;
@@ -6347,6 +6613,10 @@ function WriteFlowingBlock( $s)
 			$this->keep_block_together = 0;
 		}
 
+		if ($this->kwt && !$is_table) {	// mPDF 5.7+
+			$this->printkwtbuffer();
+			$this->kwt = false;
+		}
 
 /*-- COLUMNS --*/
 		// COLS
@@ -6913,7 +7183,7 @@ function Image($file,$x,$y,$w=0,$h=0,$type='',$link='',$paint=true, $constrain=t
 
 	  if ($this->watermarkImgBehind) {
 		$outstring = $this->watermarkImgAlpha . "\n" . $outstring . "\n" . $this->SetAlpha(1, 'Normal', true) . "\n";
-		$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.date('jY').')/', "\n".$outstring."\n".'\\1', $this->pages[$this->page]);
+		$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.$this->uniqstr.')/', "\n".$outstring."\n".'\\1', $this->pages[$this->page]);
 	  }
 	  else { $this->_out($outstring); }
 
@@ -6950,7 +7220,7 @@ function Image($file,$x,$y,$w=0,$h=0,$type='',$link='',$paint=true, $constrain=t
 		$this->AddPage($this->CurOrientation);
 		// Added to correct for OddEven Margins
 		$x=$x +$this->MarginCorrection;
-		$y = $tMargin + $this->margin_header;
+		$y = $this->tMargin;	// mPDF 5.7.3
 		$changedpage = true;
 	  }
 /*-- COLUMNS --*/
@@ -7387,7 +7657,7 @@ function Output($name='',$dest='')
 /*-- PROGRESS-BAR --*/
 	if ($this->progressBar && ($dest=='D' || $dest=='I')) {
 		if($name=='') { $name='mpdf.pdf'; }
-		$tempfile = '_tempPDF'.RAND(1,10000);
+		$tempfile = '_tempPDF'.uniqid(rand(1,100000),true);
 		//Save to local file
 		$f=fopen(_MPDF_TEMP_PATH.$tempfile.'.pdf','wb');
 		if(!$f) $this->Error('Unable to create temporary output file: '.$tempfile.'.pdf');
@@ -7499,10 +7769,10 @@ function Output($name='',$dest='')
 	// DELETE OLD TMP FILES - Housekeeping
 	// Delete any files in tmp/ directory that are >1 hrs old
 		$interval = 3600;
-		if ($handle = opendir(preg_replace('/\/$/','',_MPDF_TEMP_PATH))) {
+		if ($handle = @opendir(preg_replace('/\/$/','',_MPDF_TEMP_PATH))) {	// mPDF 5.7.3
 		   while (false !== ($file = readdir($handle))) {
-			if (!is_dir($file) && ((filemtime(_MPDF_TEMP_PATH.$file)+$interval) < time()) && ($file != "..") && ($file != ".")) { // mPDF 5.4.19
-				unlink(_MPDF_TEMP_PATH.$file);
+			if (($file != "..") && ($file != ".") && !is_dir($file) && ((filemtime(_MPDF_TEMP_PATH.$file)+$interval) < time()) && (substr($file, 0, 1) !== '.') && ($file !='dummy.txt')) { // mPDF 5.7.3
+				@unlink(_MPDF_TEMP_PATH.$file);
 			}
 		   }
 		   closedir($handle);
@@ -7576,7 +7846,7 @@ function _puthtmlheaders() {
 		else { $pntstr = ''; }
 		$html = str_replace($this->aliasNbPgGp,$pntstr,$html );	// {nbpg}
 		$html = str_replace($this->aliasNbPg,$nb,$html );	// {nb}
-		$html = preg_replace('/\{DATE\s+(.*?)\}/e',"date('\\1')",$html );
+		$html = preg_replace_callback('/\{DATE\s+(.*?)\}/', array($this, 'date_callback') ,$html );	// mPDF 5.7
 
 		$this->HTMLheaderPageLinks = array();
 		$this->HTMLheaderPageAnnots = array();
@@ -7602,7 +7872,7 @@ function _puthtmlheaders() {
 
 		// Writes over the page background but behind any other output on page
 		$os = preg_replace('/\\\\/','\\\\\\\\',$os);
-		$this->pages[$n] = preg_replace('/(___HEADER___MARKER'.date('jY').')/', "\n".$os."\n".'\\1', $this->pages[$n]);
+		$this->pages[$n] = preg_replace('/(___HEADER___MARKER'.$this->uniqstr.')/', "\n".$os."\n".'\\1', $this->pages[$n]);
 
 		$lks = $this->HTMLheaderPageLinks;
 		foreach($lks AS $lk) {
@@ -7659,7 +7929,7 @@ function _puthtmlheaders() {
 		else { $pntstr = ''; }
 		$html = str_replace($this->aliasNbPgGp,$pntstr,$html );	// {nbpg}
 		$html = str_replace($this->aliasNbPg,$nb,$html );	// {nb}
-		$html = preg_replace('/\{DATE\s+(.*?)\}/e',"date('\\1')",$html );
+		$html = preg_replace_callback('/\{DATE\s+(.*?)\}/', array($this, 'date_callback') ,$html );	// mPDF 5.7
 
 
 		$this->HTMLheaderPageLinks = array();
@@ -7670,7 +7940,6 @@ function _puthtmlheaders() {
 		$this->writingHTMLfooter = true;
 		$this->InFooter = true;
 		$this->WriteHTML($html , 4);	// parameter 4 saves output to $this->headerbuffer
-		$this->writingHTMLfooter = false;
 		$this->InFooter = false;
 		$this->Reset();
 		$this->pageoutput[$n] = array();
@@ -7680,6 +7949,7 @@ function _puthtmlheaders() {
 
 		$s = $this->PrintPageBackgrounds(-$adj);
 		$this->headerbuffer = $s . $this->headerbuffer;
+		$this->writingHTMLfooter = false;	// mPDF 5.7.3  (moved after PrintPageBackgrounds so can adjust position of images in footer)
 
 		$os = '';
 		$os .= $this->StartTransform(true)."\n";
@@ -7694,7 +7964,7 @@ function _puthtmlheaders() {
 		$os .= $this->StopTransform(true)."\n";
 		// Writes over the page background but behind any other output on page
 		$os = preg_replace('/\\\\/','\\\\\\\\',$os);
-		$this->pages[$n] = preg_replace('/(___HEADER___MARKER'.date('jY').')/', "\n".$os."\n".'\\1', $this->pages[$n]);
+		$this->pages[$n] = preg_replace('/(___HEADER___MARKER'.$this->uniqstr.')/', "\n".$os."\n".'\\1', $this->pages[$n]);
 
 		$lks = $this->HTMLheaderPageLinks;
 		foreach($lks AS $lk) {
@@ -7876,10 +8146,14 @@ function _putpages()
 			$thispage=str_replace($this->aliasNbPgGpHex,$r,$thispage);
 
 		}
-		$thispage = preg_replace('/(\s*___BACKGROUND___PATTERNS'.date('jY').'\s*)/', " ", $thispage);
-		$thispage = preg_replace('/(\s*___HEADER___MARKER'.date('jY').'\s*)/', " ", $thispage);
-		$thispage = preg_replace('/(\s*___PAGE___START'.date('jY').'\s*)/', " ", $thispage);
-		$thispage = preg_replace('/(\s*___TABLE___BACKGROUNDS'.date('jY').'\s*)/', " ", $thispage);
+		$thispage = preg_replace('/(\s*___BACKGROUND___PATTERNS'.$this->uniqstr.'\s*)/', " ", $thispage);
+		$thispage = preg_replace('/(\s*___HEADER___MARKER'.$this->uniqstr.'\s*)/', " ", $thispage);
+		$thispage = preg_replace('/(\s*___PAGE___START'.$this->uniqstr.'\s*)/', " ", $thispage);
+		$thispage = preg_replace('/(\s*___TABLE___BACKGROUNDS'.$this->uniqstr.'\s*)/', " ", $thispage);
+		// mPDF 5.7.3 TRANSFORMS
+		while (preg_match('/(\% BTR(.*?)\% ETR)/is', $thispage, $m)) {
+			$thispage = preg_replace('/(\% BTR.*?\% ETR)/is', '', $thispage, 1)."\n".$m[2];
+		}
 
 		//Page
 		$this->_newobj();
@@ -7928,10 +8202,13 @@ function _putpages()
 		}
 
 		$annotsnum = 0;
+		$embeddedfiles = array();	// mPDF 5.7.2 /EmbeddedFiles
+
 		if (isset($this->PageLinks[$n])) { $annotsnum += count($this->PageLinks[$n]); }
 /*-- ANNOTATIONS --*/
 		if (isset($this->PageAnnots[$n])) {
 			foreach ($this->PageAnnots[$n] as $k => $pl) {
+				if (!empty($pl['opt']['file'])) { $embeddedfiles[$annotsnum+1] = true ; }	// mPDF 5.7.2 /EmbeddedFiles
 				if (!empty($pl['opt']['popup']) || !empty($pl['opt']['file'])) { $annotsnum += 2 ; }
 				else { $annotsnum++; }
 				$this->PageAnnots[$n][$k]['pageobj'] = $this->n;
@@ -7952,7 +8229,7 @@ function _putpages()
 		if ($annotsnum || $formsnum) {
 			$s = '/Annots [ ';
 			for($i=0;$i<$annotsnum;$i++) {
-				$s .= ($annotid + $i) . ' 0 R ';
+				if (!isset($embeddedfiles[$i])) { $s .= ($annotid + $i) . ' 0 R '; }	// mPDF 5.7.2 /EmbeddedFiles
 			}
 			$annotid += $annotsnum;
 /*-- FORMS --*/
@@ -7974,7 +8251,7 @@ function _putpages()
 		$this->_putstream($p);
 		$this->_out('endobj');
 	}
-	$this->_putannots($n);
+	$this->_putannots();	// mPDF 5.7.2
 
 	//Pages root
 	$this->offsets[1]=strlen($this->buffer);
@@ -7991,7 +8268,7 @@ function _putpages()
 }
 
 
-function _putannots($n) {
+function _putannots() {	// mPDF 5.7.2
 	$filter=($this->compress) ? '/Filter /FlateDecode ' : '';
 	$nb=$this->page;
 	for($n=1;$n<=$nb;$n++)
@@ -9567,13 +9844,13 @@ function _beginpage($orientation,$mgl='',$mgr='',$mgt='',$mgb='',$mgh='',$mgf=''
 
 function _setAutoHeaderHeight(&$det, &$htmlh) {
   if ($this->setAutoTopMargin=='pad') {
-	if ($htmlh['h']) { $h = $htmlh['h']; }
+	if (isset($htmlh['h']) && $htmlh['h']) { $h = $htmlh['h']; }	// 5.7.3
 	else if ($det) { $h = $this->_getHFHeight($det,'H'); }
 	else { $h = 0; }
 	$this->tMargin = $this->margin_header + $h + $this->orig_tMargin;
   }
   else if ($this->setAutoTopMargin=='stretch') {
-	if ($htmlh['h']) { $h = $htmlh['h']; }
+	if (isset($htmlh['h']) && $htmlh['h']) { $h = $htmlh['h']; }	// 5.7.3
 	else if ($det) { $h = $this->_getHFHeight($det,'H'); }
 	else { $h = 0; }
 	$this->tMargin = max($this->orig_tMargin, $this->margin_header + $h + $this->autoMarginPadding);
@@ -9583,14 +9860,14 @@ function _setAutoHeaderHeight(&$det, &$htmlh) {
 
 function _setAutoFooterHeight(&$det, &$htmlf) {
   if ($this->setAutoBottomMargin=='pad') {
-	if ($htmlf['h']) { $h = $htmlf['h']; }
+	if (isset($htmlf['h']) && $htmlf['h']) { $h = $htmlf['h']; }	// 5.7.3
 	else if ($det) { $h = $this->_getHFHeight($det,'F'); }
 	else { $h = 0; }
 	$this->bMargin = $this->margin_footer + $h + $this->orig_bMargin;
 	$this->PageBreakTrigger=$this->h-$this->bMargin ;
   }
   else if ($this->setAutoBottomMargin=='stretch') {
-	if ($htmlf['h']) { $h = $htmlf['h']; }
+	if (isset($htmlf['h']) && $htmlf['h']) { $h = $htmlf['h']; }	// 5.7.3
 	else if ($det) { $h = $this->_getHFHeight($det,'F'); }
 	else { $h = 0; }
 	$this->bMargin = max($this->orig_bMargin, $this->margin_footer + $h + $this->autoMarginPadding);
@@ -9752,7 +10029,7 @@ function _getImage(&$file, $firsttime=true, $allowvector=true, $orig_srcpath=fal
 			if ($this->PDFA && !$this->PDFAauto) { $this->PDFAXwarnings[] = "JPG image may not use CMYK color space - ".$file." - (Image converted to RGB. NB This will alter the colour profile of the image.)"; }
 			$im = @imagecreatefromstring($data);
 			if ($im) {
-				$tempfile = _MPDF_TEMP_PATH.'_tempImgPNG'.RAND(1,10000).'.png';
+				$tempfile = _MPDF_TEMP_PATH.'_tempImgPNG'.md5($file).RAND(1,10000).'.png';
 				imageinterlace($im, false);
 				$check = @imagepng($im, $tempfile);
 				if (!$check) { return $this->_imageError($file, $firsttime, 'Error creating temporary file ('.$tempfile.') whilst using GD library to parse JPG(CMYK) image'); }
@@ -10057,7 +10334,7 @@ function _getImage(&$file, $firsttime=true, $allowvector=true, $orig_srcpath=fal
 		if (isset($gd['GIF Read Support']) && $gd['GIF Read Support']) {
 			$im = @imagecreatefromstring($data);
 			if ($im) {
-				$tempfile = _MPDF_TEMP_PATH.'_tempImgPNG'.RAND(1,10000).'.png';
+				$tempfile = _MPDF_TEMP_PATH.'_tempImgPNG'.md5($file).RAND(1,10000).'.png';
 				imagealphablending($im, false);
 				imagesavealpha($im, false);
 				imageinterlace($im, false);
@@ -10102,7 +10379,7 @@ function _getImage(&$file, $firsttime=true, $allowvector=true, $orig_srcpath=fal
 		if(isset($gif->m_img->m_gih->m_bLocalClr) && $gif->m_img->m_gih->m_bLocalClr) {
 			$nColors = $gif->m_img->m_gih->m_nTableSize;
 			$pal = $gif->m_img->m_gih->m_colorTable->toString();
-			if($bgColor != -1) {
+			if((isset($bgColor)) and $bgColor != -1) {	// mPDF 5.7.3
 				$bgColor = $gif->m_img->m_gih->m_colorTable->colorIndex($bgColor);
 			}
 			$colspace='Indexed';
@@ -10188,7 +10465,7 @@ function _getImage(&$file, $firsttime=true, $allowvector=true, $orig_srcpath=fal
 		if (isset($gd['PNG Support']) && $gd['PNG Support']) {
 			$im = @imagecreatefromstring($data);
 			if (!$im) { return $this->_imageError($file, $firsttime, 'Error parsing image file - image type not recognised, and not supported by GD imagecreate'); }
-			$tempfile = _MPDF_TEMP_PATH.'_tempImgPNG'.RAND(1,10000).'.png';
+			$tempfile = _MPDF_TEMP_PATH.'_tempImgPNG'.md5($file).RAND(1,10000).'.png';
 			imagealphablending($im, false);
 			imagesavealpha($im, false);
 			imageinterlace($im, false);
@@ -10312,10 +10589,10 @@ function _convImage(&$data, $colspace, $targetcs, $w, $h, $dpi, $mask) {
 					$imgdata .= chr($r).chr($g).chr($b);
 				}
 				if ($mask) {
-					$col = imagecolorsforindex($im, $rgb);
-					$gammacorr = 2.2;	// gamma correction
-					$gamma = intval((pow((((127 - $col['alpha']) * 255 / 127) / 255), $gammacorr) * 255));
-					$mimgdata .= chr($gamma);
+					// mPDF 5.7.2 Gamma correction
+					$alpha = ($rgb & 0x7F000000) >> 24;
+					if ($alpha < 127) { $mimgdata .= chr(255-($alpha * 2)); }
+					else { $mimgdata .= chr(0); }
 				}
 			}
 		}
@@ -10333,7 +10610,7 @@ function _convImage(&$data, $colspace, $targetcs, $w, $h, $dpi, $mask) {
 			$minfo = array('w'=>$w,'h'=>$h,'cs'=>'DeviceGray','bpc'=>8,'f'=>'FlateDecode','data'=>$mimgdata, 'type'=>'png',
 			'parms'=>'/DecodeParms <</Colors '.$ncols.' /BitsPerComponent 8 /Columns '.$w.'>>');
 			if ($dpi) { $minfo['set-dpi'] = $dpi; }
-			$tempfile = '_tempImgPNG'.RAND(1,10000).'.png';
+			$tempfile = '_tempImgPNG'.md5($file).RAND(1,10000).'.png';
 			$imgmask = count($this->images)+1;
 			$minfo['i']=$imgmask ;
 			$this->images[$tempfile] = $minfo;
@@ -10396,11 +10673,21 @@ function file_get_contents_by_curl($url, &$data) {
 
 
 function file_get_contents_by_socket($url, &$data) {
+	// mPDF 5.7.3
 	$timeout = 1;
 	$p = parse_url($url);
 	$file = $p['path'];
+	if ($p['scheme']=='https') {
+		$prefix = 'ssl://';
+		$port = ($p['port'] ? $p['port'] : 443);
+	}
+	else {
+		$prefix = '';
+		$port = ($p['port'] ? $p['port'] : 80);
+	}
 	if ($p['query']) { $file .= '?'.$p['query']; }
-	if(!($fh = @fsockopen($p['host'], 80, $errno, $errstr, $timeout))) { return false; }
+	if(!($fh = @fsockopen($prefix.$p['host'], $port, $errno, $errstr, $timeout))) { return false; }
+
 	$getstring =
 		"GET ".$file." HTTP/1.0 \r\n" .
 		"Host: ".$p['host']." \r\n" .
@@ -10425,7 +10712,7 @@ function file_get_contents_by_socket($url, &$data) {
 
 function _imageTypeFromString(&$data) {
 	$type = '';
-	if (substr($data, 6, 4)== 'JFIF' || substr($data, 6, 4)== 'Exif') {
+	if (substr($data, 6, 4)== 'JFIF' || substr($data, 6, 4)== 'Exif' || substr($data, 0, 2)== chr(255).chr(216)) { // 0xFF 0xD8	// mpDF 5.7.2
 		$type = 'jpeg';
 	}
 	else if (substr($data, 0, 6)== "GIF87a" || substr($data, 0, 6)== "GIF89a") {
@@ -11134,7 +11421,11 @@ function GetFullPath(&$path,$basepath='') {
 	if (!$basepath) { $basepath = $this->basepath; }
 	//Fix path value
 	$path = str_replace("\\","/",$path); //If on Windows
-	$path = preg_replace('/^\/\//','http://',$path);	// mPDF 5.6.27
+	// mPDF 5.7.2
+	if (substr($path,0,2) == "//") {
+		$tr = parse_url($basepath);
+		$path = $tr['scheme'].':'.$path;
+	}
 	$regexp = '|^./|';	// Inadvertently corrects "./path/etc" and "//www.domain.com/etc"
 	$path = preg_replace($regexp,'',$path);
 
@@ -11155,7 +11446,11 @@ function GetFullPath(&$path,$basepath='') {
 	else if( strpos($path,":/") === false || strpos($path,":/") > 10) { //It is a Local Link
 		if (substr($path,0,1) == "/") {
 			$tr = parse_url($basepath);
-			$root = $tr['scheme'].'://'.$tr['host'];
+			// mPDF 5.7.2
+			$root = '';
+			if (!empty($tr['scheme'])) { $root .= $tr['scheme'].'://'; }
+			$root .= $tr['host'];
+			$root .= ($tr['port'] ? (':'.$tr['port']) : '');	// mPDF 5.7.3
 			$path = $root . $path;
 		}
 		else { $path = $basepath . $path; }
@@ -11168,6 +11463,8 @@ function GetFullPath(&$path,$basepath='') {
 function _get_file($path) {
 	// If local file try using local path (? quicker, but also allowed even if allow_url_fopen false)
 	$contents = '';
+	// mPDF 5.7.3
+	if (strpos($path,"//") === false ) { $path = preg_replace('/\.css\?.*$/', '.css', $path); }
 	$contents = @file_get_contents($path);
 	if ($contents) { return $contents; }
 	if ($this->basepathIsLocal) {
@@ -11367,7 +11664,7 @@ function Header($content='') {
 	  if (isset($h[$side][$pos]['content']) && $h[$side][$pos]['content']) {
 		$hd = str_replace('{PAGENO}',$pgno,$h[$side][$pos]['content']);
 		$hd = str_replace($this->aliasNbPgGp,$this->nbpgPrefix.$this->aliasNbPgGp.$this->nbpgSuffix,$hd);
-		$hd = preg_replace('/\{DATE\s+(.*?)\}/e',"date('\\1')",$hd);
+		$hd = preg_replace_callback('/\{DATE\s+(.*?)\}/', array($this, 'date_callback') ,$hd);	// mPDF 5.7
 		if (isset($h[$side][$pos]['font-family']) && $h[$side][$pos]['font-family']) { $hff = $h[$side][$pos]['font-family']; }
 		else { $hff = $this->original_default_font; }
 		if (isset($h[$side][$pos]['font-size']) && $h[$side][$pos]['font-size']) { $hfsz = $h[$side][$pos]['font-size']; }
@@ -11438,10 +11735,24 @@ function Header($content='') {
 
 /*-- TABLES --*/
 function TableHeaderFooter($content='',$tablestartpage='',$tablestartcolumn ='',$horf = 'H',$level, $firstSpread=true, $finalSpread=true) {
-  if(($horf=='H' || $horf=='F') && !empty($content) && !empty($content[0])) {	// mPDF 5.6.61
+  if(($horf=='H' || $horf=='F') && !empty($content)) {	// mPDF 5.7.2
 	$table = &$this->table[1][1];
-	// Advance down page by half width of top border
 
+	// mPDF 5.7.2
+	if ($horf=='F') { // Table Footer
+		$firstrow = count($table['cells']) - $table['footernrows'];
+		$lastrow = count($table['cells']) - 1;
+	}
+   	else { 	// Table Header
+		$firstrow = 0;
+		$lastrow = $table['headernrows'] - 1;
+	}
+	if(empty($content[$firstrow])) {
+		if ($this->debug) { $this->Error("&lt;tfoot&gt; must precede &lt;tbody&gt; in a table"); }
+		else { return; }
+	}
+
+	// Advance down page by half width of top border
 	if ($horf=='H') { // Only if header
 		if ($table['borders_separate']) { $adv = $table['border_spacing_V']/2 + $table['border_details']['T']['w'] + $table['padding']['T'];  }
 		else { $adv = $table['max_cell_border_width']['T'] /2 ; }
@@ -11455,14 +11766,6 @@ function TableHeaderFooter($content='',$tablestartpage='',$tablestartcolumn ='',
 		}
 	}
 
-   if ($horf=='F') { // Table Footer
-	$firstrow = count($table['cells']) - $table['footernrows'];
-	$lastrow = count($table['cells']) - 1;
-   }
-   else { 	// Table Header
-	$firstrow = 0;
-	$lastrow = $table['headernrows'] - 1;
-   }
 
    $topy = $content[$firstrow][0]['y']-$this->y;
 
@@ -12071,7 +12374,7 @@ function _getHtmlHeight($html) {
 		$html = str_replace('{PAGENO}',$this->pagenumPrefix.$this->docPageNum($this->page).$this->pagenumSuffix,$html);
 		$html = str_replace($this->aliasNbPgGp,$this->nbpgPrefix.$this->docPageNumTotal($this->page).$this->nbpgSuffix,$html );
 		$html = str_replace($this->aliasNbPg,$this->page,$html );
-		$html = preg_replace('/\{DATE\s+(.*?)\}/e',"date('\\1')",$html );
+		$html = preg_replace_callback('/\{DATE\s+(.*?)\}/', array($this, 'date_callback') ,$html ); // mPDF 5.7
 		$this->HTMLheaderPageLinks = array();
 		$this->HTMLheaderPageAnnots = array();
 		$this->HTMLheaderPageForms = array();
@@ -12081,6 +12384,10 @@ function _getHtmlHeight($html) {
 		$this->writingHTMLheader = false;
 		$h = ($this->y - $this->margin_header);
 		$this->Reset();
+		// mPDF 5.7.2 - Clear in case Float used in Header/Footer
+		$this->blk[0]['blockContext'] = 0;
+		$this->blk[0]['float_endpos'] = 0;
+
 		$this->pageoutput[$this->page] = array();
 		$this->headerbuffer = '';
 		$this->pageBackgrounds = $savepb;
@@ -12204,7 +12511,7 @@ function SetHTMLHeaderByName($name,$side='O',$write=false) {
 
 function SetHTMLFooterByName($name,$side='O') {
 	if (!$name) { $name = '_default'; }
-	$this->SetHTMLFooter($this->pageHTMLfooters[$name],$side,$write);
+	$this->SetHTMLFooter($this->pageHTMLfooters[$name],$side);
 }
 /*-- END HTMLHEADERS-FOOTERS --*/
 
@@ -12506,7 +12813,7 @@ function Footer() {
 	  if (isset($h[$side][$pos]['content']) && $h[$side][$pos]['content']) {
 		$hd = str_replace('{PAGENO}',$pgno,$h[$side][$pos]['content']);
 		$hd = str_replace($this->aliasNbPgGp,$this->nbpgPrefix.$this->aliasNbPgGp.$this->nbpgSuffix,$hd);
-		$hd = preg_replace('/\{DATE\s+(.*?)\}/e',"date('\\1')",$hd);
+		$hd = preg_replace_callback('/\{DATE\s+(.*?)\}/', array($this, 'date_callback') ,$hd);	// mPDF 5.7
 		if (isset($h[$side][$pos]['font-family']) && $h[$side][$pos]['font-family']) { $hff = $h[$side][$pos]['font-family']; }
 		else { $hff = $this->original_default_font; }
 		if (isset($h[$side][$pos]['font-size']) && $h[$side][$pos]['font-size']) { $hfsz = $h[$side][$pos]['font-size']; }
@@ -12596,7 +12903,7 @@ function hardHyphenate($word, $maxWidth) {
 		else if ($this->FontFamily!='csymbol' && $this->FontFamily!='czapfdingbats') {
 			$p = strpos($word, "-", $offset);
 		}
-		if ($p !== false) { $poss[] = $p - count($poss); }
+		if ($p !== false) { $poss[] = $p; }	// mPDF 5.7.2
 		else { break; }
 		$offset = $p+1;
 	}
@@ -12651,7 +12958,9 @@ function softHyphenate($word, $maxWidth) {
 		else if ($this->FontFamily!='csymbol' && $this->FontFamily!='czapfdingbats') {
 			$p = strpos($word, chr(173), $offset);
 		}
-		if ($p !== false) { $poss[] = $p - count($poss); }
+		// mPDF 5.7.2
+		//if ($p !== false) { $poss[] = $p - count($poss); }
+		if ($p !== false) { $poss[] = $p; }
 		else { break; }
 		$offset = $p+1;
 	}
@@ -12915,7 +13224,7 @@ function WriteHTML($html,$sub=0,$init=true,$close=true) {
 
 /*-- CSS-PAGE --*/
 	// If page-box is set
-	if ($this->state==0 && isset($this->cssmgr->CSS['@PAGE']) && $this->cssmgr->CSS['@PAGE'] ) {
+	if ($this->state==0 && ((isset($this->cssmgr->CSS['@PAGE']) && $this->cssmgr->CSS['@PAGE']) || (isset($this->cssmgr->CSS['@PAGE>>PSEUDO>>FIRST']) && $this->cssmgr->CSS['@PAGE>>PSEUDO>>FIRST'])) ) {	// mPDF 5.7.3
 		$this->page_box['current'] = '';
 		$this->page_box['using'] = true;
 		list($pborientation,$pbmgl,$pbmgr,$pbmgt,$pbmgb,$pbmgh,$pbmgf,$hname,$fname,$bg,$resetpagenum,$pagenumstyle,$suppress,$marks,$newformat) = $this->SetPagedMediaCSS('', false, 'O');
@@ -12926,7 +13235,7 @@ function WriteHTML($html,$sub=0,$init=true,$close=true) {
 		$this->orig_bMargin = $this->bMargin = $pbmgb;
 		$this->orig_hMargin = $this->margin_header = $pbmgh;
 		$this->orig_fMargin = $this->margin_footer = $pbmgf;
-		list($pborientation,$pbmgl,$pbmgr,$pbmgt,$pbmgb,$pbmgh,$pbmgf,$hname,$fname,$bg,$resetpagenum,$pagenumstyle,$suppress,$marks,$newformat) = $this->SetPagedMediaCSS('', true, 'O');	// first page
+		list($pborientation,$pbmgl,$pbmgr,$pbmgt,$pbmgb,$pbmgh,$pbmgf,$hname,$fname,$bg,$resetpagenum,$pagenumstyle,$suppress,$marks,$newformat) = $this->SetPagedMediaCSS('', true, 'O');	// true=first page
 		$this->show_marks = $marks;
 		if ($hname && !preg_match('/^html_(.*)$/i',$hname)) $this->firstPageBoxHeader = $hname;
 		if ($fname && !preg_match('/^html_(.*)$/i',$fname)) $this->firstPageBoxFooter = $fname;
@@ -13066,7 +13375,8 @@ function WriteHTML($html,$sub=0,$init=true,$close=true) {
 					$cnt += $this->SubstituteCharsMB($a, $i, $e);
 				}
  				if ($this->biDirectional)  { 	// *RTL*
-					$e = preg_replace("/([".$this->pregRTLchars."]+)/ue", '$this->ArabJoin(stripslashes(\'\\1\'))', $e);	// *RTL*
+					// mPDF 5.7+
+					$e = preg_replace_callback("/([".$this->pregRTLchars."]+)/u", array($this, 'arabJoinPregCallback'), $e );	// *RTL*
 				}	// *RTL*
 				// Font-specific ligature substitution for Indic fonts
 				if (isset($this->CurrentFont['indic']) && $this->CurrentFont['indic']) $this->ConvertIndic($e);	// *INDIC*
@@ -13360,7 +13670,7 @@ function WriteHTML($html,$sub=0,$init=true,$close=true) {
 			if ($old_page != $new_page) {
 				$s = $this->PrintPageBackgrounds();
 				// Writes after the marker so not overwritten later by page background etc.
-				$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.date('jY').')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
+				$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.$this->uniqstr.')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
 				$this->pageBackgrounds = array();
 				$this->page = $new_page;
 				$this->ResetMargins();
@@ -14278,6 +14588,9 @@ function SetPagedMediaCSS($name='', $first, $oddEven) {
 	else { $zp = array(); }
 	if (isset($zp['SIZE'])) { unset($zp['SIZE']); }
 	if (isset($zp['SHEET-SIZE'])) { unset($zp['SHEET-SIZE']); }
+	// Disallow margin-left or -right on :FIRST	// mPDF 5.7.3
+	if (isset($zp['MARGIN-LEFT'])) { unset($zp['MARGIN-LEFT']); }
+	if (isset($zp['MARGIN-RIGHT'])) { unset($zp['MARGIN-RIGHT']); }
 	if (is_array($zp) && !empty($zp)) { $p = array_merge($p,$zp); }
 
 	// If named page
@@ -14324,6 +14637,9 @@ function SetPagedMediaCSS($name='', $first, $oddEven) {
 		else { $zp = array(); }
 		if (isset($zp['SIZE'])) { unset($zp['SIZE']); }
 		if (isset($zp['SHEET-SIZE'])) { unset($zp['SHEET-SIZE']); }
+		// Disallow margin-left or -right on :FIRST	// mPDF 5.7.3
+		if (isset($zp['MARGIN-LEFT'])) { unset($zp['MARGIN-LEFT']); }
+		if (isset($zp['MARGIN-RIGHT'])) { unset($zp['MARGIN-RIGHT']); }
 		if (is_array($zp) && !empty($zp)) { $p = array_merge($p,$zp); }
 	}
 
@@ -14350,6 +14666,7 @@ function SetPagedMediaCSS($name='', $first, $oddEven) {
 		if ($p['SIZE']['W'] > $p['SIZE']['H']) { $p['ORIENTATION'] = 'L'; }
 		else { $p['ORIENTATION'] = 'P'; }
 	}
+
 	if (is_array($p['SIZE'])) {
 		if ($p['SIZE']['W'] > $this->fw) { $p['SIZE']['W'] = $this->fw; }	// mPD 4.2 use fw not fPt
 		if ($p['SIZE']['H'] > $this->fh) { $p['SIZE']['H'] = $this->fh; }
@@ -14443,7 +14760,7 @@ function ClearFloats($clear, $blklvl=0) {
 	if ($old_page != $new_page) {
 		$s = $this->PrintPageBackgrounds();
 		// Writes after the marker so not overwritten later by page background etc.
-		$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.date('jY').')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
+		$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.$this->uniqstr.')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
 		$this->pageBackgrounds = array();
 		$this->page = $new_page;
 	}
@@ -14493,7 +14810,7 @@ function OpenTag($tag,$attr)
   // Correct tags where HTML specifies optional end tags,
   // and/or does not allow nesting e.g. P inside P, or
   if ($this->allow_html_optional_endtags) {
-    if (($tag == 'P' || $tag == 'DIV' || $tag == 'H1' || $tag == 'H2' || $tag == 'H3' || $tag == 'H4' || $tag == 'H5' || $tag == 'H6' || $tag == 'UL' || $tag == 'OL' || $tag == 'TABLE' || $tag=='PRE' || $tag=='FORM' || $tag=='ADDRESS' || $tag=='BLOCKQUOTE' || $tag=='CENTER' || $tag=='DL' || $tag == 'HR' ) && $this->lastoptionaltag == 'P') { $this->CloseTag($this->lastoptionaltag ); }
+    if (($tag == 'P' || $tag == 'DIV' || $tag == 'H1' || $tag == 'H2' || $tag == 'H3' || $tag == 'H4' || $tag == 'H5' || $tag == 'H6' || $tag == 'UL' || $tag == 'OL' || $tag == 'TABLE' || $tag=='PRE' || $tag=='FORM' || $tag=='ADDRESS' || $tag=='BLOCKQUOTE' || $tag=='CENTER' || $tag=='DL' || $tag == 'HR' || $tag=='ARTICLE' || $tag=='ASIDE' || $tag=='FIELDSET' || $tag=='HGROUP' || $tag=='MAIN' || $tag=='NAV' || $tag=='SECTION' ) && $this->lastoptionaltag == 'P') { $this->CloseTag($this->lastoptionaltag ); }	// mPDF 5.7.3
     if ($tag == 'DD' && $this->lastoptionaltag == 'DD') { $this->CloseTag($this->lastoptionaltag ); }
     if ($tag == 'DD' && $this->lastoptionaltag == 'DT') { $this->CloseTag($this->lastoptionaltag ); }
     if ($tag == 'DT' && $this->lastoptionaltag == 'DD') { $this->CloseTag($this->lastoptionaltag ); }
@@ -14758,7 +15075,6 @@ function OpenTag($tag,$attr)
 	$save_blklvl = $this->blklvl;
 	$save_blk = $this->blk;
 	$save_silp = $this->saveInlineProperties();
-	$save_spanlvl = $this->spanlvl;
 	$save_ilp = $this->InlineProperties;
 
 	// Close any open block tags
@@ -14861,7 +15177,6 @@ function OpenTag($tag,$attr)
 			unset($this->blk[$b+1]);
 			$this->OpenTag($tc,$ac);
 		}
-		$this->spanlvl = $save_spanlvl;
 		$this->InlineProperties = $save_ilp;
 		$this->restoreInlineProperties($save_silp);
 	}
@@ -14941,8 +15256,8 @@ function OpenTag($tag,$attr)
 		if (strpos($size,',')) { $size = explode(',',$size); }
 	}
 	else { $size = 'D'; }
-	if (isset($attr['POS']) && $attr['POS']) {
-		$pos = $attr['POS'];
+	if (isset($attr['POSITION']) && $attr['POSITION']) {  	// mPDF 5.7.2
+		$pos = $attr['POSITION'];
 		if (strpos($pos,',')) { $pos = explode(',',$pos); }
 	}
 	else { $pos = 'P'; }
@@ -15138,15 +15453,14 @@ function OpenTag($tag,$attr)
 	}
 /*-- END ANNOTATIONS --*/
 
-	if ($tag == 'SPAN') {
-		$this->spanlvl++;
-		$this->InlineProperties['SPAN'][$this->spanlvl] = $this->saveInlineProperties();
-		if (isset($annot)) { $this->InlineAnnots[$tag][$this->spanlvl] = $annot; }	// *ANNOTATIONS*
-	}
-	else {
-		if (!isset($this->InlineProperties[$tag])) $this->InlineProperties[$tag] = $this->saveInlineProperties(); // mPDF 5.4.13
-		if (isset($annot)) { $this->InlineAnnots[$tag] = $annot; }	// *ANNOTATIONS*
-	}
+	// mPDF 5.7.3 Inline tags
+	if (!isset($this->InlineProperties[$tag])) { $this->InlineProperties[$tag] = array($this->saveInlineProperties()); }
+	else { $this->InlineProperties[$tag][] = $this->saveInlineProperties(); }
+	if (isset($annot)) { 	// *ANNOTATIONS*
+		if (!isset($this->InlineAnnots[$tag])) { $this->InlineAnnots[$tag] = array($annot); }	// *ANNOTATIONS*
+		else { $this->InlineAnnots[$tag][] = $annot; }	// *ANNOTATIONS*
+	}	// *ANNOTATIONS*
+
 	$properties = $this->cssmgr->MergeCSS('INLINE',$tag,$attr);
 	if (!empty($properties)) $this->setCSS($properties,'INLINE');
 	break;
@@ -15290,7 +15604,7 @@ function OpenTag($tag,$attr)
 		$this->meter = new meter();
 		$svg = $this->meter->makeSVG(strtolower($tag), $type, $value, $max, $min, $optimum, $low, $high);
 		//Save to local file
-		$srcpath= _MPDF_TEMP_PATH.'_tempSVG'.RAND(1,10000).'_'.strtolower($tag).'.svg';
+		$srcpath= _MPDF_TEMP_PATH.'_tempSVG'.uniqid(rand(1,100000),true).'_'.strtolower($tag).'.svg';
 		file_put_contents($srcpath, $svg);
 		$orig_srcpath = $srcpath;
 		$this->GetFullPath($srcpath);
@@ -15418,10 +15732,8 @@ function OpenTag($tag,$attr)
     case 'DT':
     case 'DD':
     case 'FIELDSET':
-    // mPDF 5.5.22
     case 'DETAILS':
     case 'SUMMARY':
-    // mPDF 5.5.09
     case 'ARTICLE':
     case 'ASIDE':
     case 'FIGURE':
@@ -15431,6 +15743,7 @@ function OpenTag($tag,$attr)
     case 'HGROUP':
     case 'NAV':
     case 'SECTION':
+    case 'MAIN':	// mPDF 5.7.3
 	$p = $this->cssmgr->PreviewBlockCSS($tag,$attr);
 	if(isset($p['DISPLAY']) && strtolower($p['DISPLAY'])=='none') {
 		$this->blklvl++;
@@ -15484,7 +15797,6 @@ function OpenTag($tag,$attr)
 /*-- END LISTS --*/
 
 	$this->InlineProperties = array();
-	$this->spanlvl = 0;
 	$this->listjustfinished=false;
 	$this->divbegin=true;
 
@@ -15527,7 +15839,6 @@ function OpenTag($tag,$attr)
 	$save_blklvl = $this->blklvl;
 	$save_blk = $this->blk;
 	$save_silp = $this->saveInlineProperties();
-	$save_spanlvl = $this->spanlvl;
 	$save_ilp = $this->InlineProperties;
 
 	$this->blklvl++;
@@ -15564,7 +15875,6 @@ function OpenTag($tag,$attr)
 		}
 /*-- END COLUMNS --*/
 
-
 		// Must Add new page if changed page properties
 		if (isset($properties['PAGE-BREAK-BEFORE'])) {
 			if (strtoupper($properties['PAGE-BREAK-BEFORE']) == 'RIGHT') { $this->AddPage($this->CurOrientation,'NEXT-ODD','','','','','', '','', '','','','','','',0,0,0,0,$pagesel); }
@@ -15621,7 +15931,6 @@ function OpenTag($tag,$attr)
 				unset($this->blk[$b+1]);
 				$this->OpenTag($tc,$ac);
 			}
-			$this->spanlvl = $save_spanlvl;
 			$this->InlineProperties = $save_ilp;
 			$this->restoreInlineProperties($save_silp);
 		}
@@ -16749,6 +17058,9 @@ function OpenTag($tag,$attr)
 		$extraheight = $objattr['padding_top'] + $objattr['padding_bottom'] + $objattr['margin_top'] + $objattr['margin_bottom'] + $objattr['border_top']['w'] + $objattr['border_bottom']['w'];
 		$extrawidth = $objattr['padding_left'] + $objattr['padding_right'] + $objattr['margin_left'] + $objattr['margin_right'] + $objattr['border_left']['w'] + $objattr['border_right']['w'];
 
+		// mPDF 5.7.3 TRANSFORMS
+		if (isset($properties['BACKGROUND-COLOR']) && $properties['BACKGROUND-COLOR'] != '') { $objattr['bgcolor'] = $this->ConvertColor($properties['BACKGROUND-COLOR']); }
+
 /*-- BACKGROUNDS --*/
 		if(isset($properties['GRADIENT-MASK']) && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/',$properties['GRADIENT-MASK'])) {
 	     		$objattr['GRADIENT-MASK'] = $properties['GRADIENT-MASK'];
@@ -16866,6 +17178,10 @@ function OpenTag($tag,$attr)
 		  }
 		}
 /*-- END CSS-IMAGE-FLOAT --*/
+		// mPDF 5.7.3 TRANSFORMS
+		if (isset($properties['TRANSFORM']) && !$this->ColActive && !$this->kwt) {
+			$objattr['transform'] = $properties['TRANSFORM'];
+		}
 
 		$e = "\xbb\xa4\xactype=image,objattr=".serialize($objattr)."\xbb\xa4\xac";
 
@@ -17128,7 +17444,7 @@ function OpenTag($tag,$attr)
 	if ($this->cacheTables) {
 		$this->packTableData = true;	// required for cacheTables
 		$this->simpleTables = false;  // Cannot co-exist with cacheTables
-		$table['cache'] = _MPDF_TEMP_PATH.'_tempTblCache'.RAND(1,1000000).'.dat';
+		$table['cache'] = _MPDF_TEMP_PATH.'_tempTblCache'.uniqid(rand(1,100000),true).'.dat';
 		$fh = fopen($table['cache'] , "wb") or $this->Error("When using cacheTables, you must have read/write access to cache files (".$table['cache'] .")");
 		fwrite($fh, "\x00");
 		fclose($fh);
@@ -17168,7 +17484,6 @@ function OpenTag($tag,$attr)
 	$table['decimal_align'] = false;	// mPDF 5.6.13
 	$this->Reset();
 	$this->InlineProperties = array();
-	$this->spanlvl = 0;
 	$table['nc'] = $table['nr'] = 0;
 	$this->tablethead = 0;
 	$this->tabletfoot = 0;
@@ -17358,10 +17673,7 @@ function OpenTag($tag,$attr)
 		$table['borders_separate'] = false;
 	}
 
-	if (!$table['borders_separate']) { $table['border_spacing_H'] = $table['border_spacing_V'] = 0; }
-	else if (isset($attr['CELLSPACING'])) {
-		$table['border_spacing_H'] = $table['border_spacing_V'] = $this->ConvertSize($attr['CELLSPACING'],$this->blk[$this->blklvl]['inner_width']);
-	}
+	// mPDF 5.7.3
 	if (isset($properties['BORDER-SPACING-H'])) {
 		$table['border_spacing_H'] = $this->ConvertSize($properties['BORDER-SPACING-H'],$this->blk[$this->blklvl]['inner_width'],$this->FontSize,false);
 	}
@@ -17369,6 +17681,8 @@ function OpenTag($tag,$attr)
 		$table['border_spacing_V'] = $this->ConvertSize($properties['BORDER-SPACING-V'],$this->blk[$this->blklvl]['inner_width'],$this->FontSize,false);
 	}
 
+	if (!$table['borders_separate']) { $table['border_spacing_H'] = $table['border_spacing_V'] = 0; }
+
 	if (isset($properties['EMPTY-CELLS'])) {
 		$table['empty_cells'] = strtolower($properties['EMPTY-CELLS']); 	// 'hide'  or 'show'
 	}
@@ -17604,7 +17918,6 @@ function OpenTag($tag,$attr)
 	$this->lastoptionaltag = $tag; // Save current HTML specified optional endtag
 	$this->cssmgr->tbCSSlvl++;
 	$this->InlineProperties = array();
-	$this->spanlvl = 0;
 	$this->tdbegin = true;
 	$this->col++;
 	while (isset($this->cell[$this->row][$this->col])) { $this->col++; }
@@ -17966,6 +18279,7 @@ function OpenTag($tag,$attr)
 	for($l=$this->col; $l < $this->col+$cs ;$l++) {
 		if ($l-$this->col) $this->cell[$this->row][$l] = 0;
 	}
+
 	if (isset($attr['ROWSPAN']) && $attr['ROWSPAN']>1)	$rs = $this->cell[$this->row][$this->col]['rowspan']	= $attr['ROWSPAN'];
 	for ($k=$this->row ; $k < $this->row+$rs ;$k++) {
 		for($l=$this->col; $l < $this->col+$cs ;$l++) {
@@ -18254,10 +18568,7 @@ function OpenTag($tag,$attr)
 
 function _getListStyle($ls) {
  	if (stristr($ls,'decimal')) { return '1'; }
-/*  CSS3 list-styles numeric (selected) + I added tamil
-arabic-indic | bengali | devanagari | gujarati | gurmukhi | kannada | malayalam | oriya | persian | telugu | thai | urdu
-*/
-	else if (preg_match('/(disc|circle|square|arabic-indic|bengali|devanagari|gujarati|gurmukhi|kannada|malayalam|oriya|persian|tamil|telugu|thai|urdu)/i',$ls,$m)) {
+	else if (preg_match('/(disc|circle|square|arabic-indic|bengali|devanagari|gujarati|gurmukhi|kannada|malayalam|oriya|persian|tamil|telugu|thai|urdu|cambodian|khmer|lao)/i',$ls,$m)) {
 		return strtolower(trim($m[1]));
 	}
 	else if (stristr($ls,'lower-roman')) { return 'i'; }
@@ -18292,19 +18603,21 @@ function CloseTag($tag)
 	|| $tag=='MARK'  || $tag=='TIME'  || $tag=='PROGRESS'  || $tag=='METER'
 	) {	// mPDF 5.5.09
 
-	if ($tag == 'SPAN') {
-		if (isset($this->InlineProperties['SPAN'][$this->spanlvl]) && $this->InlineProperties['SPAN'][$this->spanlvl]) { $this->restoreInlineProperties($this->InlineProperties['SPAN'][$this->spanlvl]); }
-		unset($this->InlineProperties['SPAN'][$this->spanlvl]);
-		if (isset($this->InlineAnnots['SPAN'][$this->spanlvl]) && $this->InlineAnnots['SPAN'][$this->spanlvl]) { $annot = $this->InlineAnnots['SPAN'][$this->spanlvl]; }	// *ANNOTATIONS*
-		unset($this->InlineAnnots['SPAN'][$this->spanlvl]);	// *ANNOTATIONS*
-		$this->spanlvl--;
-	}
-	else {
+	// mPDF 5.7.3 Inline tags
+	if ($tag=='PROGRESS'  || $tag=='METER') {
 		if (isset($this->InlineProperties[$tag]) && $this->InlineProperties[$tag]) { $this->restoreInlineProperties($this->InlineProperties[$tag]); }
 		unset($this->InlineProperties[$tag]);
 		if (isset($this->InlineAnnots[$tag]) && $this->InlineAnnots[$tag]) { $annot = $this->InlineAnnots[$tag]; }	// *ANNOTATIONS*
 		unset($this->InlineAnnots[$tag]);	// *ANNOTATIONS*
 	}
+	else {
+		if (isset($this->InlineProperties[$tag]) && count($this->InlineProperties[$tag])) {
+			$this->restoreInlineProperties(array_pop($this->InlineProperties[$tag]));
+		}
+		if (isset($this->InlineAnnots[$tag]) && count($this->InlineAnnots[$tag])) { 	// *ANNOTATIONS*
+			$annot = array_pop($this->InlineAnnots[$tag]); 	// *ANNOTATIONS*
+		}	// *ANNOTATIONS*
+	}
 
 /*-- ANNOTATIONS --*/
 	if (isset($annot)) {
@@ -18409,13 +18722,12 @@ function CloseTag($tag)
 
 
 	// *********** BLOCKS ********************
-	// mPDF 5.4.18
     if($tag=='P' || $tag=='DIV' || $tag=='H1' || $tag=='H2' || $tag=='H3' || $tag=='H4' || $tag=='H5' || $tag=='H6' || $tag=='PRE'
 	 || $tag=='FORM' || $tag=='ADDRESS' || $tag=='BLOCKQUOTE' || $tag=='CENTER' || $tag=='DT'  || $tag=='DD'  || $tag=='DL'
 	|| $tag=='CAPTION' || $tag=='FIELDSET'
 	|| $tag=='ARTICLE' || $tag=='ASIDE' || $tag=='FIGURE' || $tag=='FIGCAPTION' || $tag=='FOOTER' || $tag=='HEADER' || $tag=='HGROUP'
-	|| $tag=='NAV' || $tag=='SECTION'  || $tag=='DETAILS' || $tag=='SUMMARY'
-	) { 	// mPDF 5.5.09	// mPDF 5.5.22
+	|| $tag=='MAIN' || $tag=='NAV' || $tag=='SECTION'  || $tag=='DETAILS' || $tag=='SUMMARY'
+	) { 	// mPDF 5.7.3
 
 	$this->ignorefollowingspaces = true; //Eliminate exceeding left-side spaces
 	$this->blockjustfinished=true;
@@ -18487,7 +18799,7 @@ function CloseTag($tag)
 		if ($old_page != $new_page) {
 			$s = $this->PrintPageBackgrounds();
 			// Writes after the marker so not overwritten later by page background etc.
-			$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.date('jY').')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
+			$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.$this->uniqstr.')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
 			$this->pageBackgrounds = array();
 			$this->page = $new_page;
 			$this->ResetMargins();
@@ -18546,7 +18858,7 @@ function CloseTag($tag)
 		// If width not set, here would need to adjust and output buffer
 		$s = $this->PrintPageBackgrounds();
 		// Writes after the marker so not overwritten later by page background etc.
-		$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.date('jY').')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
+		$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.$this->uniqstr.')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
 		$this->pageBackgrounds = array();
 		$this->Reset();
 		$this->pageoutput[$this->page] = array();
@@ -18578,7 +18890,7 @@ function CloseTag($tag)
 		// If width not set, here would need to adjust and output buffer
 		$s = $this->PrintPageBackgrounds();
 		// Writes after the marker so not overwritten later by page background etc.
-		$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.date('jY').')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
+		$this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS'.$this->uniqstr.')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
 		$this->pageBackgrounds = array();
 		$this->Reset();
 		$this->pageoutput[$this->page] = array();
@@ -18641,7 +18953,6 @@ function CloseTag($tag)
 		$save_blklvl = $this->blklvl;
 		$save_blk = $this->blk;
 		$save_silp = $this->saveInlineProperties();
-		$save_spanlvl = $this->spanlvl;
 		$save_ilp = $this->InlineProperties;
 		if ($this->blklvl>1) {
 			// Close any open block tags
@@ -18660,26 +18971,12 @@ function CloseTag($tag)
 		if ($page_break_after == 'RIGHT') { $this->AddPage($this->CurOrientation,'NEXT-ODD','','','','','', '','', '','','','','','',0,0,0,0,$pagesel); }
 		else if ($page_break_after == 'LEFT') { $this->AddPage($this->CurOrientation,'NEXT-EVEN','','','','','', '','', '','','','','','',0,0,0,0,$pagesel); }
 		else { $this->AddPage($this->CurOrientation,'','','','','','', '','', '','','','','','',0,0,0,0,$pagesel); }
+		// mPDF 5.7.3
 		if (!$this->restoreBlockPagebreaks) {
 			$this->blklvl = 0;
 			$this->lastblocklevelchange = 0;
-			$this->blk = array();
-			$this->initialiseBlock($this->blk[0]);
-			$this->blk[0]['width'] =& $this->pgwidth;
-			$this->blk[0]['inner_width'] =& $this->pgwidth;
-			$this->blk[0]['blockContext'] = $this->blockContext;
-			$properties = $this->cssmgr->MergeCSS('BLOCK','BODY','');
-			$this->setCSS($properties,'','BODY');
-			$this->blklvl++;
-			$currblk =& $this->blk[$this->blklvl];
-			$prevblk =& $this->blk[$this->blklvl-1];
-
-			$this->initialiseBlock($currblk);
-			$currblk['tag'] = $tag;
-			$currblk['attr'] = $attr;
-
 			$this->Reset();
-			$properties = $this->cssmgr->MergeCSS('BLOCK',$tag,$attr);
+			$this->restoreInlineProperties($this->blk[0]['InlineProperties']);
 		}
 /*-- COLUMNS --*/
 		if ($save_cols) {
@@ -18701,7 +18998,6 @@ function CloseTag($tag)
 				unset($this->blk[$b+1]);
 				$this->OpenTag($tc,$ac);
 			}
-			$this->spanlvl = $save_spanlvl;
 			$this->InlineProperties = $save_ilp;
 			$this->restoreInlineProperties($save_silp);
 		}
@@ -18829,6 +19125,46 @@ function CloseTag($tag)
 	unset($this->cssmgr->tablecascadeCSS[$this->cssmgr->tbCSSlvl]);
 	$this->cssmgr->tbCSSlvl--;
 	$this->ignorefollowingspaces = true; //Eliminate exceeding left-side spaces
+
+	// mPDF 5.7.3
+	// In case a colspan (on a row after first row) exceeded number of columns in table
+	for ($k=0; $k < $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['nr']; $k++) {
+		for($l=0; $l < $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['nc']; $l++) {
+			if (!isset($this->cell[$k][$l])) {
+				for ($n=$l-1; $n>=0; $n--) {
+					if (isset($this->cell[$k][$n]) && $this->cell[$k][$n]!=0) { break; }
+				}
+				$this->cell[$k][$l] = array(
+					'a' => 'C',
+					'va' => 'M',
+					'R' => false,
+					'nowrap' => false,
+					'bgcolor' => false,
+					'padding' => array('L' => false, 'R' => false, 'T' => false, 'B' => false),
+					'gradient' => false,
+					's' => 0,
+					'maxs' => 0,
+					'textbuffer' => array(),
+					'dfs' => $this->FontSize,
+				);
+
+				if (!$this->simpleTables){
+					$this->cell[$k][$l]['border'] = 0;
+					$this->cell[$k][$l]['border_details']['R'] = array('s' => 0, 'w' => 0, 'c' => false, 'style' => 'none', 'dom' => 0);
+					$this->cell[$k][$l]['border_details']['L'] = array('s' => 0, 'w' => 0, 'c' => false, 'style' => 'none', 'dom' => 0);
+					$this->cell[$k][$l]['border_details']['T'] = array('s' => 0, 'w' => 0, 'c' => false, 'style' => 'none', 'dom' => 0);
+					$this->cell[$k][$l]['border_details']['B'] = array('s' => 0, 'w' => 0, 'c' => false, 'style' => 'none', 'dom' => 0);
+					$this->cell[$k][$l]['border_details']['mbw'] = array('BL' =>0,'BR' =>0,'RT' =>0,'RB' =>0,'TL' =>0,'TR' =>0,'LT' =>0,'LB' =>0);
+					if ($this->packTableData) {
+						$this->cell[$k][$l]['borderbin'] = $this->_packCellBorder($this->cell[$k][$l]);
+						unset($this->cell[$k][$l]['border']);
+						unset($this->cell[$k][$l]['border_details']);
+					}
+				}
+			}
+		}
+	}
+
 	$this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['cells'] = $this->cell;
 	$this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['wc'] = array_pad(array(),$this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['nc'],array('miw'=>0,'maw'=>0));
 	$this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['hr'] = array_pad(array(),$this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['nr'],0);
@@ -18924,6 +19260,12 @@ function CloseTag($tag)
 
 		// Reset lower level table
 		$this->base_table_properties = $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['baseProperties'];
+		// mPDF 5.7.3
+		$this->default_font = $this->base_table_properties['FONT-FAMILY'];
+		$this->SetFont($this->default_font,'',0,false);
+		$this->default_font_size = $this->ConvertSize($this->base_table_properties['FONT-SIZE'])*(_MPDFK);
+   		$this->SetFontSize($this->default_font_size,false);
+
 		$this->cell = $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['cells'];
 		// mPDF 5.4.10
 		if (isset($this->cell['PARENTCELL'])) {
@@ -19453,7 +19795,6 @@ function CloseTag($tag)
 		$save_blklvl = $this->blklvl;
 		$save_blk = $this->blk;
 		$save_silp = $this->saveInlineProperties();
-		$save_spanlvl = $this->spanlvl;
 		$save_ilp = $this->InlineProperties;
 		if ($this->blklvl>1) {
 			// Close any open block tags
@@ -19504,7 +19845,6 @@ function CloseTag($tag)
 				unset($this->blk[$b+1]);
 				$this->OpenTag($tc,$ac);
 			}
-			$this->spanlvl = $save_spanlvl;
 			$this->InlineProperties = $save_ilp;
 			$this->restoreInlineProperties($save_silp);
 		}
@@ -19851,6 +20191,19 @@ arabic-indic | bengali | cambodian | devanagari | gujarati | gurmukhi | kannada
 		  $list_item_marker = $rnum . $this->list_number_suffix;
   	        $blt_width = $this->GetStringWidth(str_repeat($this->dec2other(5, $cp),strlen($maxnum)).$this->list_number_suffix);
 		  break;
+          case 'khmer':
+          case 'cambodian':
+		  $cp = 0x17E0;
+		  $rnum = $this->dec2other($num, $cp);
+		  $list_item_marker = $rnum . $this->list_number_suffix;
+  	        $blt_width = $this->GetStringWidth(str_repeat($this->dec2other(3, $cp),strlen($maxnum)).$this->list_number_suffix);
+		  break;
+          case 'lao':
+		  $cp = 0x0ED0;
+		  $rnum = $this->dec2other($num, $cp);
+		  $list_item_marker = $rnum . $this->list_number_suffix;
+  	        $blt_width = $this->GetStringWidth(str_repeat($this->dec2other(6, $cp),strlen($maxnum)).$this->list_number_suffix);
+		  break;
 	    default:
 		  if ($this->listDir == 'rtl') { $list_item_marker = $this->list_number_suffix . $num; }
 		  else { $list_item_marker = $num . $this->list_number_suffix; }
@@ -20093,13 +20446,13 @@ function printbuffer($arrayaux,$blockstate=0,$is_table=false,$is_list=false)
 	if(isset($vetor[15])) { 	 // Word spacing
 		$this->wSpacingCSS = $vetor[15];
 		if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
-			$this->minwSpacing = $this->ConvertSize($this->wSpacingCSS,$this->FontSize);
+			$this->minwSpacing = $this->ConvertSize($this->wSpacingCSS,$this->FontSize)/$this->shrin_k; // mPDF 5.7.3
 		}
 	}
 	if(isset($vetor[14])) { 	 // Letter spacing
 		$this->lSpacingCSS = $vetor[14];
 		if (($this->lSpacingCSS || $this->lSpacingCSS==='0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
-			$this->fixedlSpacing = $this->ConvertSize($this->lSpacingCSS,$this->FontSize);
+			$this->fixedlSpacing = $this->ConvertSize($this->lSpacingCSS,$this->FontSize)/$this->shrin_k; // mPDF 5.7.3
 		}
 	}
 	if(isset($vetor[13])) { 	 // Font Kerning
@@ -20116,6 +20469,12 @@ function printbuffer($arrayaux,$blockstate=0,$is_table=false,$is_list=false)
 	{
 		$this->textparam = $vetor[9] ;			// mPDF 5.6.14
 		$this->SetTextOutline($this->textparam);		// mPDF 5.6.07
+		// mPDF 5.7.3  inline text-decoration parameters
+		if ($is_table && $this->shrin_k) {
+			if (isset($this->textparam['text-baseline'])) { $this->textparam['text-baseline'] /= $this->shrin_k; }
+			if (isset($this->textparam['decoration-baseline'])) { $this->textparam['decoration-baseline'] /= $this->shrin_k; }
+			if (isset($this->textparam['decoration-fontsize'])) { $this->textparam['decoration-fontsize'] /= $this->shrin_k; }
+		}
 	}
 	if(isset($vetor[8]) and $vetor[8] === true) // strike-through the text
 	{
@@ -20660,10 +21019,10 @@ function _setBorderLine($b, $k=1) {
 	$this->SetLineWidth($b['w']/$k);
 	$this->SetDColor($b['c']);
 	if ($b['c'][0]==5) {	// RGBa
-		$this->SetAlpha($b['c'][4], 'Normal', false, 'S')."\n";
+		$this->SetAlpha(ord($b['c'][4])/100, 'Normal', false, 'S')."\n";	// mPDF 5.7.2
 	}
 	else if ($b['c'][0]==6) {	// CMYKa
-		$this->SetAlpha($b['c'][5], 'Normal', false, 'S')."\n";
+		$this->SetAlpha(ord($b['c'][5])/100, 'Normal', false, 'S')."\n";	// mPDF 5.7.2
 	}
 }
 
@@ -21978,6 +22337,10 @@ function ReadCharset($html) {
 
 function setCSS($arrayaux,$type='',$tag='') {	// type= INLINE | BLOCK | LIST // tag= BODY
 	if (!is_array($arrayaux)) return; //Removes PHP Warning
+	// mPDF 5.7.3  inline text-decoration parameters
+	$preceeding_fontkey = $this->FontFamily . $this->FontStyle;
+	$preceeding_fontsize = $this->FontSize;
+
 	// Set font size first so that e.g. MARGIN 0.83em works on font size for this element
 	if (isset($arrayaux['FONT-SIZE'])) {
 		$v = $arrayaux['FONT-SIZE'];
@@ -22340,31 +22703,42 @@ function setCSS($arrayaux,$type='',$tag='') {	// type= INLINE | BLOCK | LIST //
 
 		case 'VERTICAL-ALIGN': //super and sub only dealt with here e.g. <SUB> and <SUP>
 			switch (strtoupper($v)) {
-				case 'SUPER':
+			  case 'SUPER':
                         $this->SUP=true;
                         $this->SUB=false;	// mPDF 5.6.07
-                       break;
-				case 'SUB':
+ 				// mPDF 5.7.3  inline text-decoration parameters
+				if (isset($this->textparam['text-baseline'])) { $this->textparam['text-baseline'] += ($this->baselineSup)*$preceeding_fontsize; }
+				else { $this->textparam['text-baseline'] = ($this->baselineSup)*$preceeding_fontsize; }
+				break;
+			  case 'SUB':
                         $this->SUB=true;
                         $this->SUP=false;	// mPDF 5.6.07
-                       break;
-				case 'BASELINE': 	// mPDF 5.6.07
+				// mPDF 5.7.3  inline text-decoration parameters
+				if (isset($this->textparam['text-baseline'])) { $this->textparam['text-baseline'] += ($this->baselineSub)*$preceeding_fontsize; }
+				else { $this->textparam['text-baseline'] = ($this->baselineSub)*$preceeding_fontsize; }
+				break;
+			  case 'BASELINE': 	// mPDF 5.6.07
                         $this->SUB=false;
                         $this->SUP=false;
-                       break;
-			}
-			break;
-
-		case 'TEXT-DECORATION': // none underline line-through (strikeout) //Does not support: overline, blink
-			if (stristr($v,'LINE-THROUGH')) {
-					$this->strike = true;
-			}
-			else if (stristr($v,'UNDERLINE')) {
-            			$this->SetStyle('U',true);
-			}
-			else if (stristr($v,'NONE')) {
-            			$this->SetStyle('U',false);
-					$this->strike = false;	// mPDF 5.6.07
+				// mPDF 5.7.3  inline text-decoration parameters
+				if (isset($this->textparam['text-baseline'])) { unset($this->textparam['text-baseline']); }
+				break;
+			  // mPDF 5.7.3  inline text-decoration parameters
+			  default:
+				$lh = $this->_computeLineheight($this->blk[$this->blklvl]['line_height']);
+				$sz = $this->ConvertSize($v,$lh,$this->FontSize,false);
+                        $this->SUP=false;
+                        $this->SUB=false;
+				if ($sz) {
+					if ($sz > 0) {
+                        		$this->SUP=true;
+					}
+					else {
+						$this->SUB=true;
+					}
+					if (isset($this->textparam['text-baseline'])) { $this->textparam['text-baseline'] += $sz; }
+					else { $this->textparam['text-baseline'] = $sz; }
+				}
 			}
 			break;
 
@@ -22462,6 +22836,42 @@ function setCSS($arrayaux,$type='',$tag='') {	// type= INLINE | BLOCK | LIST //
 
 
    }//end of foreach
+
+
+	// mPDF 5.7.3  inline text-decoration parameters
+	// Needs to be set at the end - after vertical-align = super/sub, so that textparam['text-baseline'] is set
+	if (isset($arrayaux['TEXT-DECORATION'])) {
+		$v = $arrayaux['TEXT-DECORATION']; // none underline line-through (strikeout) //Does not support: overline, blink
+		if (stristr($v,'LINE-THROUGH')) {
+			$this->strike = true;
+			// mPDF 5.7.3  inline text-decoration parameters
+			if (isset($this->textparam['text-baseline'])) { $this->textparam['decoration-baseline'] = $this->textparam['text-baseline']; }
+			else { $this->textparam['decoration-baseline'] = 0; }
+			$this->textparam['decoration-fontkey'] = $this->FontFamily . $this->FontStyle;
+			$this->textparam['decoration-fontsize'] = $this->FontSize;
+			$this->textparam['s-decoration']['color'] = strtoupper($this->TextColor);	// change 0 0 0 rg to 0 0 0 RG
+		}
+		if (stristr($v,'UNDERLINE')) {
+            	$this->SetStyle('U',true);
+			// mPDF 5.7.3  inline text-decoration parameters
+			if (isset($this->textparam['text-baseline'])) { $this->textparam['decoration-baseline'] = $this->textparam['text-baseline']; }
+			else { $this->textparam['decoration-baseline'] = 0; }
+			$this->textparam['decoration-fontkey'] = $this->FontFamily . $this->FontStyle;
+			$this->textparam['decoration-fontsize'] = $this->FontSize;
+			$this->textparam['u-decoration']['color'] = strtoupper($this->TextColor);	// change 0 0 0 rg to 0 0 0 RG
+		}
+		if (stristr($v,'NONE')) {
+            	$this->SetStyle('U',false);
+			$this->strike = false;
+			// mPDF 5.7.3  inline text-decoration parameters
+			if (isset($this->textparam['decoration-baseline'])) { unset($this->textparam['decoration-baseline']); }
+			if (isset($this->textparam['decoration-fontkey'])) { unset($this->textparam['decoration-fontkey']); }
+			if (isset($this->textparam['decoration-fontsize'])) { unset($this->textparam['decoration-fontsize']); }
+			if (isset($this->textparam['u-decoration'])) { unset($this->textparam['u-decoration']); }
+			if (isset($this->textparam['s-decoration'])) { unset($this->textparam['s-decoration']); }
+		}
+	}
+
 }
 
 /*-- END HTML-CSS --*/
@@ -22525,7 +22935,7 @@ function DisableTags($str='')
   if ($str == '') //enable all tags
   {
 	//Insert new supported tags in the long string below.
-	$this->enabledtags = "<span><s><strike><del><bdo><big><small><ins><cite><acronym><font><sup><sub><b><u><i><a><strong><em><code><samp><tt><kbd><var><q><table><thead><tfoot><tbody><tr><th><td><ol><ul><li><dl><dt><dd><form><input><select><textarea><option><div><p><h1><h2><h3><h4><h5><h6><pre><center><blockquote><address><hr><img><br><indexentry><indexinsert><bookmark><watermarktext><watermarkimage><tts><ttz><tta><column_break><columnbreak><newcolumn><newpage><page_break><pagebreak><formfeed><columns><toc><tocentry><tocpagebreak><pageheader><pagefooter><setpageheader><setpagefooter><sethtmlpageheader><sethtmlpagefooter><annotation><template><jpgraph><barcode><dottab><caption><textcircle><fieldset><legend><article><aside><figure><figcaption><footer><header><hgroup><nav><section><mark><details><summary><meter><progress><time>";	// mPDF 5.5.09
+	$this->enabledtags = "<span><s><strike><del><bdo><big><small><ins><cite><acronym><font><sup><sub><b><u><i><a><strong><em><code><samp><tt><kbd><var><q><table><thead><tfoot><tbody><tr><th><td><ol><ul><li><dl><dt><dd><form><input><select><textarea><option><div><p><h1><h2><h3><h4><h5><h6><pre><center><blockquote><address><hr><img><br><indexentry><indexinsert><bookmark><watermarktext><watermarkimage><tts><ttz><tta><column_break><columnbreak><newcolumn><newpage><page_break><pagebreak><formfeed><columns><toc><tocentry><tocpagebreak><pageheader><pagefooter><setpageheader><setpagefooter><sethtmlpageheader><sethtmlpagefooter><annotation><template><jpgraph><barcode><dottab><caption><textcircle><fieldset><legend><article><aside><figure><figcaption><footer><header><hgroup><nav><section><mark><main><details><summary><meter><progress><time>";	// mPDF 5.7.3
   }
   else
   {
@@ -22713,13 +23123,13 @@ function TableWordWrap($maxwidth, $forcewrap = 0, $textbuffer = '', $def_fontsiz
       if(isset($chunk[15])) { 	 // Word spacing
 		$this->wSpacingCSS = $chunk[15];
 		if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
-			$this->minwSpacing = $this->ConvertSize($this->wSpacingCSS,$this->FontSize);
+			$this->minwSpacing = $this->ConvertSize($this->wSpacingCSS,$this->FontSize)/$this->shrin_k; // mPDF 5.7.3
 		}
 	}
       if(isset($chunk[14])) { 	 // Letter spacing
 		$this->lSpacingCSS = $chunk[14];
 		if (($this->lSpacingCSS || $this->lSpacingCSS==='0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
-			$this->fixedlSpacing = $this->ConvertSize($this->lSpacingCSS,$this->FontSize);
+			$this->fixedlSpacing = $this->ConvertSize($this->lSpacingCSS,$this->FontSize)/$this->shrin_k; // mPDF 5.7.3
 		}
 	}
       if(isset($chunk[13])) { 	 // Font Kerning
@@ -23083,13 +23493,13 @@ function TableCheckMinWidth($maxwidth, $forcewrap = 0, $textbuffer) {
 	      if(isset($chunk[15])) { 	 // Word spacing
 			$this->wSpacingCSS = $chunk[15];
 			if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
-				$this->minwSpacing = $this->ConvertSize($this->wSpacingCSS,$this->FontSize);
+				$this->minwSpacing = $this->ConvertSize($this->wSpacingCSS,$this->FontSize)/$this->shrin_k; // mPDF 5.7.3
 			}
 		}
 	      if(isset($chunk[14])) { 	 // Letter spacing
 			$this->lSpacingCSS = $chunk[14];
 			if (($this->lSpacingCSS || $this->lSpacingCSS==='0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
-				$this->fixedlSpacing = $this->ConvertSize($this->lSpacingCSS,$this->FontSize);
+				$this->fixedlSpacing = $this->ConvertSize($this->lSpacingCSS,$this->FontSize)/$this->shrin_k; // mPDF 5.7.3
 			}
 		}
 	      if(isset($chunk[13])) { 	 // Font Kerning
@@ -23613,7 +24023,6 @@ function _tableColumnWidth(&$table,$firstpass=false){
 					$extrcw = $bl/2 + $br/2 + $c['padding']['L'] + $c['padding']['R'];
 				   }
 				}
-
 				//$mw = $this->GetStringWidth('W') + $extrcw ;
 				$mw = 0;
 				// mPDF 5.6.13  Decimal point alignment
@@ -23653,7 +24062,6 @@ function _tableColumnWidth(&$table,$firstpass=false){
 					if (isset($c['nestedmaw'])) { $c['nestedmaw'] += $extrcw; }
 				}
 
-
 				// If minimum width has already been set by a nested table or inline object (image/form), use it
 				if (isset($c['nestedmiw']) && $this->table[1][1]['overflow']!='visible') { $miw = $c['nestedmiw']; }
 				else  { $miw = $mw; }
@@ -23695,7 +24103,7 @@ function _tableColumnWidth(&$table,$firstpass=false){
 					if ($miw<$c['w'])	{ $c['miw'] = $c['w']; }	// Cell min width = that specified
 					if ($miw>$c['w'])	{ $c['miw'] = $c['w'] = $miw; } // If width specified is less than minimum allowed (W) increase it
 					if (!isset($wc['w'])) { $wc['w'] = 1; }		// If the Col width is not specified = set it to 1
-
+					$c['maw'] = $c['w'];	// mPDF 5.7.3
 				}
 				else { $c['miw'] = $miw; }	// If cell width not specified -> set Cell min width it to minimum allowed (W)
 
@@ -23742,7 +24150,6 @@ function _tableColumnWidth(&$table,$firstpass=false){
 		}//rows
 	}//columns
 
-
 	// COLUMN SPANS
 	$wc = &$table['wc'];
 	foreach ($listspan as $span) {
@@ -23784,7 +24191,7 @@ function _tableColumnWidth(&$table,$firstpass=false){
 			}
 			else {
 				$wi = $c['miw'] - $wis;
-				foreach ($list as $k) { $wc[$k]['miw'] += ($wc[$k]['miw']/$wisa)*$wi; }
+				foreach ($list as $k) { if (!isset($wc[$k]['w']) || !$wc[$k]['w']) $wc[$k]['miw'] += ($wc[$k]['miw']/$wisa)*$wi; }	// mPDF 5.7.2
 			}
 		}
 		if ($c['maw'] > $was) {
@@ -23918,7 +24325,6 @@ function _tableColumnWidth(&$table,$firstpass=false){
 	if ((isset($table['maw']) && $checkmaxwidth > $table['maw']) || !isset($table['maw'])) { $table['maw'] = $checkmaxwidth; }
 	$table['tl'] = $totallength ;
 
-
 	if (!$this->tableCJK) {
 		if ($this->table_rotate) {
 			$mxw = $this->tbrot_maxw;
@@ -24013,8 +24419,18 @@ function _tableWidth(&$table){
 	}
 	// if table width is set and is > allowed width
 	if (isset($table['w']) && $table['w'] > $temppgwidth) { $table['w'] = $temppgwidth; }
+
 	// IF the table width is now set - Need to distribute columns widths
-	if (isset($table['w'])) {
+	// mPDF 5.7.3
+	// If the table width is already set to the maximum width (e.g. nested table), then use maximum column widths exactly
+	if (isset($table['w']) && ($table['w'] == $tablewidth) && !$percentages_set) {
+		// This sets the columns all to maximum width
+		for ($i=0;$i<$numcols;$i++) {
+			$widthcols[$i] = $widthcols[$i]['maw'];
+		}
+	}
+	// Else If the table width is set distribute width using algorithm
+	else if (isset($table['w'])) {
 		$wis = $wisa = 0;
 		$list = array();
 		$notsetlist = array();
@@ -24049,7 +24465,7 @@ function _tableWidth(&$table){
 				for($k=0;$k<$numcols;$k++) {
 					$spareratio = ($table['l'][$k] / $totaltextlength); //  gives ratio to divide up free space
 					// Don't allocate more than Maximum required width - save rest in surplus
-					if ($widthcols[$k]['miw'] + ($wi * $spareratio) > $widthcols[$k]['maw']) {
+					if ($widthcols[$k]['miw'] + ($wi * $spareratio) >= $widthcols[$k]['maw']) {	// mPDF 5.7.3
 						$surplus += ($wi * $spareratio) - ($widthcols[$k]['maw']-$widthcols[$k]['miw']);
 						$widthcols[$k]['miw'] = $widthcols[$k]['maw'];
 					}
@@ -24066,7 +24482,7 @@ function _tableWidth(&$table){
 				foreach ($list as $k) {
 					$spareratio = ($table['l'][$k] / $totalatextlength); //  gives ratio to divide up free space
 					// Don't allocate more than Maximum required width - save rest in surplus
-					if ($widthcols[$k]['miw'] + ($wi * $spareratio) > $widthcols[$k]['maw']) {
+					if ($widthcols[$k]['miw'] + ($wi * $spareratio) >= $widthcols[$k]['maw']) {	// mPDF 5.7.3
 						$surplus += ($wi * $spareratio) - ($widthcols[$k]['maw']-$widthcols[$k]['miw']);
 						$widthcols[$k]['miw'] = $widthcols[$k]['maw'];
 					}
@@ -24127,7 +24543,6 @@ function _tableWidth(&$table){
 			}
 		   }
 		}
-
 	}
 	else { //table has no width defined
 		$table['w'] = $tablewidth;
@@ -25212,8 +25627,8 @@ function _fixTableBorders(&$table){
 			else {
 				$cbord = &$cells[$i][$j];
 			}
-
-  			if (!$cbord['border'] && isset($table['border']) && $table['border'] && $this->table_border_attr_set) {
+			// mPDF 5.7.3
+  			if (!$cbord['border'] && $cbord['border']!==0 && isset($table['border']) && $table['border'] && $this->table_border_attr_set) {
 				$cbord['border'] = $table['border'];
 				$cbord['border_details'] = $table['border_details'];
 			}
@@ -25830,7 +26245,7 @@ function _tableWrite(&$table, $split=false, $startrow=0, $startcol=0, $splitpg=0
 	  }
 	}
 
-	if ($level==1) { $this->_out('___TABLE___BACKGROUNDS'.date('jY')); }
+	if ($level==1) { $this->_out('___TABLE___BACKGROUNDS'.$this->uniqstr); }
 	$tableheaderadj = 0;
 	$tablefooteradj = 0;
 
@@ -26093,12 +26508,12 @@ function _tableWrite(&$table, $split=false, $startrow=0, $startcol=0, $splitpg=0
 						if ($this->tableBackgrounds) {
 						   $s = $this->PrintTableBackgrounds();
 	   					   if ($this->bufferoutput) {
-							$this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS'.date('jY').')/', '\\1'."\n".$s."\n", $this->headerbuffer);
-							$this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS'.date('jY').')/', " ", $this->headerbuffer );
+							$this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS'.$this->uniqstr.')/', '\\1'."\n".$s."\n", $this->headerbuffer);
+							$this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS'.$this->uniqstr.')/', " ", $this->headerbuffer );
 						   }
 						   else {
-							$this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS'.date('jY').')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
-							$this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS'.date('jY').')/', " ", $this->pages[$this->page]);
+							$this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS'.$this->uniqstr.')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
+							$this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS'.$this->uniqstr.')/', " ", $this->pages[$this->page]);
 						   }
 						   $this->tableBackgrounds = array();
 						}
@@ -26120,7 +26535,7 @@ function _tableWrite(&$table, $split=false, $startrow=0, $startcol=0, $splitpg=0
 
 						$this->AddPage($this->CurOrientation);
 
-						$this->_out('___TABLE___BACKGROUNDS'.date('jY'));
+						$this->_out('___TABLE___BACKGROUNDS'.$this->uniqstr);
 
 
 						if ($this->tableClipPath ) { $this->_out($this->tableClipPath); }
@@ -26989,16 +27404,16 @@ function _tableWrite(&$table, $split=false, $startrow=0, $startcol=0, $splitpg=0
 	if ($this->tableBackgrounds && $level == 1) {
 	   $s = $this->PrintTableBackgrounds();
 	   if ($this->table_rotate && !$this->processingHeader && !$this->processingFooter) {
-		$this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS'.date('jY').')/', '\\1'."\n".$s."\n", $this->tablebuffer);
-		if ($level == 1) { $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS'.date('jY').')/', " ", $this->tablebuffer); }
+		$this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS'.$this->uniqstr.')/', '\\1'."\n".$s."\n", $this->tablebuffer);
+		if ($level == 1) { $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS'.$this->uniqstr.')/', " ", $this->tablebuffer); }
 	   }
 	   else if ($this->bufferoutput) {
-		$this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS'.date('jY').')/', '\\1'."\n".$s."\n", $this->headerbuffer);
-		if ($level == 1) { $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS'.date('jY').')/', " ", $this->headerbuffer ); }
+		$this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS'.$this->uniqstr.')/', '\\1'."\n".$s."\n", $this->headerbuffer);
+		if ($level == 1) { $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS'.$this->uniqstr.')/', " ", $this->headerbuffer ); }
 	   }
 	   else {
-		$this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS'.date('jY').')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
-		if ($level == 1) { $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS'.date('jY').')/', " ", $this->pages[$this->page]); }
+		$this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS'.$this->uniqstr.')/', '\\1'."\n".$s."\n", $this->pages[$this->page]);
+		if ($level == 1) { $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS'.$this->uniqstr.')/', " ", $this->pages[$this->page]); }
 	   }
 	   $this->tableBackgrounds = array();
 	}
@@ -27577,13 +27992,35 @@ function _putresources() {
 	}
 
 /*-- BACKGROUNDS --*/
-	if (isset($this->gradients) AND (count($this->gradients) > 0)) {
+	if ((isset($this->gradients) AND (count($this->gradients) > 0)) || ($this->enableImports && count($this->tpls))) {	// mPDF 5.7.3
 		$this->_out('/Shading <<');
 		foreach ($this->gradients as $id => $grad) {
 			$this->_out('/Sh'.$id.' '.$grad['id'].' 0 R');
 		}
-		$this->_out('>>');
+		// mPDF 5.7.3
+		// If a shading dictionary is in an object (tpl) imported from another PDF, it needs to be included
+		// in the document resources, as well as the object resources
+		// Otherwise get an error in some PDF viewers
+		if ($this->enableImports && count($this->tpls)) {
+			foreach($this->tpls as $tplidx => $tpl) {
+				if (isset($tpl['resources'])) {
+					$this->current_parser =& $tpl['parser'];
+					reset ($tpl['resources'][1]);
+					while (list($k, $v) = each($tpl['resources'][1])) {
+						if ($k == '/Shading') {
+							while (list($k2, $v2) = each($v[1])) {
+								$this->_out($k2 . " ",false);
+								$this->pdf_write_value($v2);
+							}
+						}
+					}
+
 
+				}
+			}
+		}
+
+		$this->_out('>>');
 /*
 		// ??? Not needed !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 		$this->_out('/Pattern <<');
@@ -28125,14 +28562,9 @@ function TOCpagebreak($tocfont='', $tocfontsize='', $tocindent='', $TOCusePaging
 }
 
 function TOC_Entry($txt, $level=0, $toc_id=0) {
-		$txt = $this->purify_utf8_text($txt);
-		if ($this->text_input_as_HTML) {
-			$txt = $this->all_entities_to_utf8($txt);
-		}
-		if ($this->usingCoreFont) { $txt = mb_convert_encoding($txt,$this->mb_enc,'UTF-8'); }
+		// mPDF 5.7.2
   		if ($this->ColActive) { $ily = $this->y0; } else { $ily = $this->y; }	// use top of columns
 
-		// mPDF 5.6.19  mPDF 5.6.25	mPDF 5.6.37
 		if (!class_exists('tocontents', false)) { include(_MPDF_PATH.'classes/tocontents.php'); }
 		if (empty($this->tocontents)) { $this->tocontents = new tocontents($this); }
 		$linkn = $this->AddLink();
@@ -28147,7 +28579,7 @@ function TOC_Entry($txt, $level=0, $toc_id=0) {
 
 /*-- RTL --*/
   		if ($this->biDirectional)  {
-			$txt = preg_replace("/([".$this->pregRTLchars."]+)/ue", '$this->ArabJoin(stripslashes(\'\\1\'))', $txt );
+			$txt = preg_replace_callback("/([".$this->pregRTLchars."]+)/u", array($this, 'arabJoinPregCallback'), $txt );	// mPDF 5.7+
 		}
 /*-- END RTL --*/
 		if (strtoupper($toc_id)=='ALL') { $toc_id = '_mpdf_all'; }
@@ -28478,7 +28910,7 @@ function DeletePages($start_page, $end_page=-1) {
 		// HTML Headers & Footers
 		if (count($this->saveHTMLHeader)) {
 			foreach($this->saveHTMLHeader AS $p=>$v) {
-				if($p>end_page) { $newarr[($p - $n_tod)] = $this->saveHTMLHeader[$p]; }
+				if($p>$end_page) { $newarr[($p - $n_tod)] = $this->saveHTMLHeader[$p]; }	// mPDF 5.7.3
 				else if($p<$start_page) { $newarr[$p] = $this->saveHTMLHeader[$p]; }
 			}
 			ksort($newarr);
@@ -28573,7 +29005,7 @@ function IndexEntry($txt, $xref='') {
 		if ($this->keep_block_together) {
 			if (isset($this->ktReference[$i]['t']) && $this->ktReference[$i]['t']==$txt){
 				$Present=1;
-				if (!in_array($this->page,$this->ktReference[$i]['p'])) {
+				if ($this->page != $this->ktReference[$i]['op']) {	// mPDF 5.7.2
 					$this->ktReference[$i]['op'] = $this->page;
 				}
 			}
@@ -28582,7 +29014,7 @@ function IndexEntry($txt, $xref='') {
 		else if ($this->table_rotate) {
 			if (isset($this->tbrot_Reference[$i]['t']) && $this->tbrot_Reference[$i]['t']==$txt){
 				$Present=1;
-				if (!in_array($this->page,$this->tbrot_Reference[$i]['p'])) {
+				if ($this->page != $this->tbrot_Reference[$i]['op']) {	// mPDF 5.7.2
 					$this->tbrot_Reference[$i]['op'] = $this->page;
 				}
 			}
@@ -28590,7 +29022,7 @@ function IndexEntry($txt, $xref='') {
 		else if ($this->kwt) {
 			if (isset($this->kwt_Reference[$i]['t']) && $this->kwt_Reference[$i]['t']==$txt){
 				$Present=1;
-				if (!in_array($this->page,$this->kwt_Reference[$i]['p'])) {
+				if ($this->page != $this->kwt_Reference[$i]['op']) {	// mPDF 5.7.2
 					$this->kwt_Reference[$i]['op'] = $this->page;
 				}
 			}
@@ -28600,7 +29032,7 @@ function IndexEntry($txt, $xref='') {
 		else if ($this->ColActive) {
 			if (isset($this->col_Reference[$i]['t']) && $this->col_Reference[$i]['t']==$txt){
 				$Present=1;
-				if (!in_array($this->page,$this->col_Reference[$i]['p'])) {
+				if ($this->page != $this->col_Reference[$i]['op']) {	// mPDF 5.7.2
 					$this->col_Reference[$i]['op'] = $this->page;
 				}
 			}
@@ -28736,7 +29168,7 @@ function CreateIndex($NbCol=1, $reffontsize='', $linespacing='', $offset=3, $use
 /*-- RTL --*/
 			// Change Arabic + Persian. to Presentation Forms
    			if ($this->biDirectional)  {
-				$this->Reference[$i]['t'] = preg_replace("/([".$this->pregRTLchars."]+)/ue", '$this->ArabJoin(stripslashes(\'\\1\'))', $this->Reference[$i]['t'] );
+				$this->Reference[$i]['t'] = preg_replace_callback("/([".$this->pregRTLchars."]+)/u", array($this, 'arabJoinPregCallback'), $this->Reference[$i]['t'] );	// mPDF 5.7+
 			}
 /*-- END RTL --*/
 
@@ -29019,7 +29451,7 @@ function NewColumn() {
 
 function printcolumnbuffer() {
    // Columns ended (but page not ended) -> try to match all columns - unless disabled by using a custom column-break
-   if (!$this->ColActive && $this->ColumnAdjust && !$this->keepColumns) {
+   if (!$this->ColActive && $this->ColumnAdjust && !$this->keepColumns) {	// mPDF 5.7.2
 	// Calculate adjustment to add to each column to calculate rel_y value
 	$this->ColDetails[0]['add_y'] = 0;
 	$last_col = 0;
@@ -29123,12 +29555,14 @@ function printcolumnbuffer() {
 		$yadj = ($s['rel_y'] - $s['y']) - ($last_col_bottom)+$this->y0;
 		// callback function
 		$t = $s['s'];
-		$t = preg_replace('/BT (\d+\.\d\d+) (\d+\.\d\d+) Td/e',"\$this->columnAdjustAdd('Td',_MPDFK,$xadj,$yadj,'\\1','\\2')",$t);
-		$t = preg_replace('/(\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) ([\-]{0,1}\d+\.\d\d+) re/e',"\$this->columnAdjustAdd('re',_MPDFK,$xadj,$yadj,'\\1','\\2','\\3','\\4')",$t);
-		$t = preg_replace('/(\d+\.\d\d+) (\d+\.\d\d+) l/e',"\$this->columnAdjustAdd('l',_MPDFK,$xadj,$yadj,'\\1','\\2')",$t);
-		$t = preg_replace('/q (\d+\.\d\d+) 0 0 (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) cm \/(I|FO)/e',"\$this->columnAdjustAdd('img',_MPDFK,$xadj,$yadj,'\\1','\\2','\\3','\\4','\\5')",$t);
-		$t = preg_replace('/(\d+\.\d\d+) (\d+\.\d\d+) m/e',"\$this->columnAdjustAdd('draw',_MPDFK,$xadj,$yadj,'\\1','\\2')",$t);
-		$t = preg_replace('/(\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) c/e',"\$this->columnAdjustAdd('bezier',_MPDFK,$xadj,$yadj,'\\1','\\2','\\3','\\4','\\5','\\6')",$t);
+
+		// mPDF 5.7+
+		$t = $this->columnAdjustPregReplace('Td', $xadj, $yadj, '/BT (\d+\.\d\d+) (\d+\.\d\d+) Td/', $t);
+		$t = $this->columnAdjustPregReplace('re', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) ([\-]{0,1}\d+\.\d\d+) re/', $t);
+		$t = $this->columnAdjustPregReplace('l', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) l/', $t);
+		$t = $this->columnAdjustPregReplace('img', $xadj, $yadj, '/q (\d+\.\d\d+) 0 0 (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) cm \/(I|FO)/', $t);
+		$t = $this->columnAdjustPregReplace('draw', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) m/', $t);
+		$t = $this->columnAdjustPregReplace('bezier',$xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) c/', $t);
 
 		$this->columnbuffer[$key]['s'] = $t;
 		$this->columnbuffer[$key]['newcol'] = $newcolumn;
@@ -29483,6 +29917,16 @@ function printcolumnbuffer() {
    $this->breakpoints = array();
 }
 
+// mPDF 5.7+
+function columnAdjustPregReplace($type, $xadj, $yadj, $pattern, $subject) {
+	preg_match($pattern, $subject, $matches);
+	if (!isset($matches[3])) { $matches[3] = 0; }
+	if (!isset($matches[4])) { $matches[4] = 0; }
+	if (!isset($matches[5])) { $matches[5] = 0; }
+	if (!isset($matches[6])) { $matches[6] = 0; }
+	return str_replace($matches[0], $this->columnAdjustAdd($type, _MPDFK, $xadj, $yadj, $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]), $subject);
+}
+
 /*-- END COLUMNS --*/
 
 
@@ -30144,6 +30588,11 @@ function reverse_letters($str) {
 	return $this->mb_strrev($str, $this->mb_enc);
 }
 
+// mPDF 5.7+
+function reverse_letters_preg_callback($matches) {
+	return $this->reverse_letters($matches[1]);
+}
+
 function magic_reverse_dir(&$chunk, $join=true, $dir) {
    if ($this->usingCoreFont) { return 0; }
    if ($this->biDirectional)  {
@@ -30153,7 +30602,7 @@ function magic_reverse_dir(&$chunk, $join=true, $dir) {
 
 	// Change Arabic + Persian. to Presentation Forms
 	if ($join) {
-		$chunk = preg_replace("/([".$pregRTLchars."]+)/ue", '$this->ArabJoin(stripslashes(\'\\1\'))', $chunk );
+		$chunk = preg_replace_callback("/([".$pregRTLchars."]+)/u", array($this, 'arabJoinPregCallback'), $chunk );	// mPDF 5.7+
 	}
 	$contains_rtl = false;
 	$all_rtl = true;
@@ -30213,7 +30662,7 @@ function magic_reverse_dir(&$chunk, $join=true, $dir) {
 						}
 						else {
 							// Reverse numerals only to RTL
-							$sbit = preg_replace("/([\x{0660}-\x{066C}0-9]+[\x{0660}-\x{066C}0-9\.,:\/]*[\x{0660}-\x{066C}0-9]+)/ue", '$this->reverse_letters(\'\\1\')', $sbit ); // mPDF 5.6.32 // mPDF 5.6.46
+							$sbit = preg_replace_callback("/([\x{0660}-\x{066C}0-9]+[\x{0660}-\x{066C}0-9\.,:\/]*[\x{0660}-\x{066C}0-9]+)/u", array($this, 'reverse_letters_preg_callback'), $sbit );	// mPDF 5.7+
 							$sbits[$sbitkey] = $sbit;
 						}
 					}
@@ -30225,7 +30674,7 @@ function magic_reverse_dir(&$chunk, $join=true, $dir) {
 				}
 				else {
 					// Reverse numerals only to RTL
-					$bit = preg_replace("/([\x{0660}-\x{066C}0-9]+[\x{0660}-\x{066C}0-9\.,:\/]*[\x{0660}-\x{066C}0-9]+)/ue", '$this->reverse_letters(\'\\1\')', $bit );	// mPDF 5.6.32 // mPDF 5.6.46
+					$bit = preg_replace_callback("/([\x{0660}-\x{066C}0-9]+[\x{0660}-\x{066C}0-9\.,:\/]*[\x{0660}-\x{066C}0-9]+)/u", array($this, 'reverse_letters_preg_callback'), $bit );	// mPDF 5.7+
 					$bits[$bitkey] = $bit;
 				}
 			}
@@ -30236,7 +30685,7 @@ function magic_reverse_dir(&$chunk, $join=true, $dir) {
 		$contains_rtl = true;
 
 		// Un-Reverse numerals back to ltr
-		$chunk = preg_replace("/([\x{0660}-\x{066C}0-9]+[\x{0660}-\x{066C}0-9\.,:\/]*[\x{0660}-\x{066C}0-9]+)/ue", '$this->reverse_letters(\'\\1\')', $chunk );	// mPDF 5.6.13 // mPDF 5.6.32 // mPDF 5.6.46
+		$chunk = preg_replace_callback("/([\x{0660}-\x{066C}0-9]+[\x{0660}-\x{066C}0-9\.,:\/]*[\x{0660}-\x{066C}0-9]+)/u", array($this, 'reverse_letters_preg_callback'), $chunk );	// mPDF 5.7+
 		if ($dir == 'rtl') {
 			if ($endSpace) { $chunk = ' '.$chunk; }
 			if ($initSpace) { $chunk .= ' '; }
@@ -30656,8 +31105,6 @@ function purify_utf8($html,$lo=true) {
 	// Only exception - leaves low ASCII entities e.g. &lt; &amp; etc.
 	// Leaves in particular &lt; to distinguish from tag marker
 	if (!$this->is_utf8($html)) {
-        $e = new Exception();
-        print $e->getTraceAsString();
 		echo "<p><b>HTML contains invalid UTF-8 character(s)</b></p>";
 		while (mb_convert_encoding(mb_convert_encoding($html, "UTF-32", "UTF-8"), "UTF-8", "UTF-32") != $html) {
 			$a = iconv('UTF-8', 'UTF-8', $html);
@@ -30689,11 +31136,7 @@ function purify_utf8($html,$lo=true) {
 function purify_utf8_text($txt) {
 	// For TEXT
 	// Make sure UTF-8 string of characters
-    if ($txt === null) $txt = '';
-    if (!$this->is_utf8($txt)) {
-        $e = new Exception();
-        print $e->getTraceAsString();
-        $this->Error("Text contains invalid UTF-8 character(s)"); }
+	if (!$this->is_utf8($txt)) { $this->Error("Text contains invalid UTF-8 character(s)"); }
 
 	$txt = preg_replace("/\r/", "", $txt );
 
@@ -31177,6 +31620,30 @@ function transformRotate($angle, $x='', $y='', $returnstring=false) {
 	if ($returnstring) { return($this->_transform($tm, true)); }
 	else { $this->_transform($tm); }
 }
+// mPDF 5.7.3 TRANSFORMS
+function transformSkew($angle_x, $angle_y, $x='', $y='', $returnstring=false) {
+	if ($x === '') {
+		$x = $this->x;
+	}
+	if ($y === '') {
+		$y = $this->y;
+	}
+	$angle_x = -$angle_x;
+	$angle_y = -$angle_y;
+	$x *= _MPDFK;
+	$y = ($this->h - $y) * _MPDFK;
+	//calculate elements of transformation matrix
+	$tm = array();
+	$tm[0] = 1;
+	$tm[1] = tan(deg2rad($angle_y));
+	$tm[2] = tan(deg2rad($angle_x));
+	$tm[3] = 1;
+	$tm[4] = -$tm[2] * $y;
+	$tm[5] = -$tm[1] * $x;
+	//skew the coordinate system
+	if ($returnstring) { return($this->_transform($tm, true)); }
+	else { $this->_transform($tm); }
+}
 function _transform($tm, $returnstring=false) {
 	if ($returnstring) { return(sprintf('%.4F %.4F %.4F %.4F %.4F %.4F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5])); }
 	else { $this->_out(sprintf('%.4F %.4F %.4F %.4F %.4F %.4F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5])); }
@@ -31216,7 +31683,7 @@ function AutoFont($html) {
 
 /*-- CJK-FONTS --*/
 		if ($this->autoFontGroups & AUTOFONT_CJK) {
-			$e = preg_replace("/([".$this->pregCJKchars.$extra."]*[".$this->pregCJKchars."][".$this->pregCJKchars.$extra."]*)/ue", '$this->replaceCJK(stripslashes(\'\\1\'))', $e);
+			$e = preg_replace_callback("/([".$this->pregCJKchars .$extra."]*[".$this->pregCJKchars ."][".$this->pregCJKchars .$extra."]*)/u", array($this, 'replaceCJKPregCallback'), $e );	// mPDF 5.7+
 		}
 /*-- END CJK-FONTS --*/
 
@@ -31225,7 +31692,7 @@ function AutoFont($html) {
 			// HEBREW
 			$e = preg_replace("/([".$this->pregHEBchars .$extra."]*[".$this->pregHEBchars ."][".$this->pregHEBchars .$extra."]*)/u", "\xef\xbf\xb0span lang=\"he\"\xef\xbf\xb1\\1\xef\xbf\xb0/span\xef\xbf\xb1", $e);
 			// All Arabic
-			$e = preg_replace("/([".$this->pregARABICchars .$extra."]*[".$this->pregARABICchars ."][".$this->pregARABICchars .$extra."]*)/ue", '$this->replaceArabic(stripslashes(\'\\1\'))', $e);
+			$e = preg_replace_callback("/([".$this->pregARABICchars .$extra."]*[".$this->pregARABICchars ."][".$this->pregARABICchars .$extra."]*)/u", array($this, 'replaceArabicPregCallback'), $e );	// mPDF 5.7+
 		}
 /*-- END RTL --*/
 
@@ -31302,6 +31769,11 @@ function replaceCJK($str) {
 	}
 	return $str;
 }
+
+// mPDF 5.7+
+function replaceCJKPregCallback($matches) {
+	return $this->replaceCJK(stripslashes($matches[1]));
+}
 /*-- END CJK-FONTS --*/
 
 /*-- RTL --*/
@@ -31341,6 +31813,11 @@ function replaceArabic($str) {
 	return $str;
 }
 
+// mPDF 5.7+
+function replaceArabicPregCallback($matches) {
+	return $this->replaceArabic(stripslashes($matches[1]));
+}
+
 // ARABIC ===========================
 // mPDF 5.4.08
 function InitArabic() {
@@ -31639,6 +32116,11 @@ function ArabJoin($str) {
 	return $s;
 }
 
+// mPDF 5.7+
+function arabJoinPregCallback($matches) {
+	return $this->ArabJoin(stripslashes($matches[1]));
+}
+
 // mPDF 5.4.08
 function get_arab_glyphs($char, $type) {
 	if ($type>0 && isset($this->arabGlyphs[$char])) {
@@ -32049,6 +32531,22 @@ function ConvertSize($size=5,$maxsize=0,$fontsize=false,$usefontsize=true){
   return $size;
 }
 
+// mPDF 5.7.3 TRANSFORMS
+function ConvertAngle($s, $makepositive=true) {
+	if (preg_match('/([\-]*[0-9\.]+)(deg|grad|rad)/i',$s,$m)) {
+		$angle = $m[1] + 0;
+		if (strtolower($m[2])=='deg') { $angle = $angle; }
+		else if (strtolower($m[2])=='grad') { $angle *= (360/400); }
+		else if (strtolower($m[2])=='rad') { $angle = rad2deg($angle); }
+		while($angle >= 360) { $angle -= 360; }
+		while($angle <= -360) { $angle += 360; }
+		if ($makepositive) {	// always returns an angle between 0 and 360deg
+			if ($angle < 0) { $angle += 360; }
+		}
+	}
+	else { $angle = $s + 0; }
+	return $angle;
+}
 
 function lesser_entity_decode($html) {
   //supports the most used entity codes (only does ascii safe characters)
@@ -32084,7 +32582,7 @@ function AdjustHTML($html, $tabSpaces=8) {
 	preg_match_all("/(<svg.*?<\/svg>)/si", $html, $svgi);
 	if (count($svgi[0])) {
 		for($i=0;$i<count($svgi[0]);$i++) {
-			$file = _MPDF_TEMP_PATH.'_tempSVG'.RAND(1,10000).'_'.$i.'.svg';
+			$file = _MPDF_TEMP_PATH.'_tempSVG'.uniqid(rand(1,100000),true).'_'.$i.'.svg';
 			//Save to local file
 			file_put_contents($file, $svgi[0][$i]);
 			$html = str_replace($svgi[0][$i], '<img src="'.$file.'" />', $html); 	// mPDF 5.5.18
@@ -32147,7 +32645,7 @@ function AdjustHTML($html, $tabSpaces=8) {
 	$html = preg_replace('/[\t]/',' ',$html); //replace tabs by spaces
 
 	// Converts < to &lt; when not a tag
-	$html = preg_replace('/<([^!\/a-zA-Z])/i','&lt;\\1',$html);
+	$html = preg_replace('/<([^!\/a-zA-Z_:])/i','&lt;\\1',$html);	// mPDF 5.7.3
 	$html = preg_replace("/[ ]+/",' ',$html);
 
 	$html = preg_replace('/\/li>\s+<\/(u|o)l/i','/li></\\1l',$html);
@@ -32160,12 +32658,14 @@ function AdjustHTML($html, $tabSpaces=8) {
 	$iterator = 0;
 	while($thereispre) //Recover <pre attributes>content</pre>
 	{
-		$temp[2][$iterator] = preg_replace("/^([^\n\t]*?)\t/me", "stripslashes('\\1') . str_repeat(' ',  ( $tabSpaces - (mb_strlen(stripslashes('\\1')) % $tabSpaces))  )",$temp[2][$iterator]);
+		$temp[2][$iterator] = preg_replace('/<([^!\/a-zA-Z_:])/','&lt;\\1',$temp[2][$iterator]);	// mPDF 5.7.2	// mPDF 5.7.3
+		$temp[2][$iterator] = preg_replace_callback("/^([^\n\t]*?)\t/m", array($this, 'tabs2spaces_callback'), $temp[2][$iterator]);	// mPDF 5.7+
 		$temp[2][$iterator] = preg_replace('/\t/',str_repeat(" ",$tabSpaces),$temp[2][$iterator]);
 
 		$temp[2][$iterator] = preg_replace('/\n/',"<br />",$temp[2][$iterator]);
 		$temp[2][$iterator] = str_replace('\\',"\\\\",$temp[2][$iterator]);
-		$html = preg_replace('#<pre(.*?)>(.*?)</pre>#si','<erp'.$temp[1][$iterator].'>'.$temp[2][$iterator].'</erp>',$html,1);
+		//$html = preg_replace('#<pre(.*?)>(.*?)</pre>#si','<erp'.$temp[1][$iterator].'>'.$temp[2][$iterator].'</erp>',$html,1);
+		$html = preg_replace('#<pre(.*?)>(.*?)</pre>#si','<erp'.$temp[1][$iterator].'>'.str_replace('$','\$',$temp[2][$iterator]).'</erp>',$html,1);	// mPDF 5.7+
 		$thereispre--;
 		$iterator++;
 	}
@@ -32193,6 +32693,14 @@ function AdjustHTML($html, $tabSpaces=8) {
 
 	return $html;
 }
+// mPDF 5.7+
+function tabs2spaces_callback($matches) {
+	return (stripslashes($matches[1]) . str_repeat(' ', $this->tabSpaces - (mb_strlen(stripslashes($matches[1])) % $this->tabSpaces)));
+}
+// mPDF 5.7+
+function date_callback($matches) {
+	return date($matches[1]);
+}
 
 /*-- LISTS --*/
 function dec2other($num, $cp) {
diff --git a/include/mpdf/tmp/.keep b/include/mpdf/tmp/.keep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/include/mpdf/ttfontdata/.keep b/include/mpdf/ttfontdata/.keep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/include/mysqli.php b/include/mysqli.php
index e8bfeb32ebcfa7f6acd5fe26991a6fc07b32c136..71681975cd8bdd8e1281a3290eac9234690fa6af 100644
--- a/include/mysqli.php
+++ b/include/mysqli.php
@@ -76,6 +76,19 @@ function db_connect($host, $user, $passwd, $options = array()) {
 
     @db_set_variable('sql_mode', '');
 
+    // Start a new transaction -- for performance. Transactions are always
+    // committed at shutdown (below)
+    $__db->autocommit(false);
+
+    // Auto commit the transaction at shutdown and re-enable statement-level
+    // autocommit
+    register_shutdown_function(function() {
+        global $__db, $err;
+        if (!$__db->commit())
+            $err = 'Unable to save changes to database';
+        $__db->autocommit(true);
+    });
+
     // Use connection timing to seed the random number generator
     Misc::__rand_seed((microtime(true) - $start) * 1000000);
 
@@ -176,6 +189,10 @@ function db_query($query, $logError=true, $buffered=true) {
     return $res;
 }
 
+function db_query_unbuffered($sql, $logError=false) {
+    return db_query($sql, $logError, true);
+}
+
 function db_squery($query) { //smart db query...utilizing args and sprintf
 
     $args  = func_get_args();
@@ -250,8 +267,12 @@ function db_free_result($res) {
 }
 
 function db_output($var) {
+    static $no_magic_quotes = null;
+
+    if (!isset($no_magic_quotes))
+        $no_magic_quotes = !function_exists('get_magic_quotes_runtime') || !get_magic_quotes_runtime();
 
-    if(!function_exists('get_magic_quotes_runtime') || !get_magic_quotes_runtime()) //Sucker is NOT on - thanks.
+    if ($no_magic_quotes) //Sucker is NOT on - thanks.
         return $var;
 
     if (is_array($var))
diff --git a/include/plugins/updates.pem b/include/plugins/updates.pem
new file mode 100644
index 0000000000000000000000000000000000000000..9f4077be363d71b3615867622fd1cf1cf899499f
--- /dev/null
+++ b/include/plugins/updates.pem
@@ -0,0 +1,20 @@
+-----BEGIN PUBLIC KEY-----
+MIIDRjCCAjkGByqGSM44BAEwggIsAoIBAQCdMklcYXqGlNYXZ5bS808qOS6U9S5z
+IQcCrf2Hzs6OLmTUDkLxKuvmoBVMu7Tkbb6TY4ne+G9npWih4OfpVsvY22T13sf2
+EBcX0jOslcm+Bc5eN4dmgjs17iuf14oMkM8WdlVceT1tVqfKJnJm3i3U/+x5SDUY
+x6UhbOgMygemfIoFtqTbaMvAmype8HnflIxRoL25uZ44Hx7eef6zpOqYVXM8VQq3
+RNXXfNmoxiMNhVrSK18LE8D8h4ABMzDg/pxFt2fbf2IrNFAV+h2MSfF+ueLcrEfy
+XbtWLx7DdxqASEASwVLGm3vJslLBBvBDfGhTSNMVGk1XWBIeHfsALPV9AiEA39Qn
+ksDDQIL5Ed7Q9mYHDqM23mtJPxi2L478HmU3zY8CggEAE1UcB7QrR/bLWgzX/fbp
+xzVonCJElkxrx1rviKkjwAAAPurCFy2bQNRPMp/e7DFVwAtouQf3i2JWtRNeyHOC
+dxDKrspfCDOdovRHkWYOxXJCztesMGcUAHo/WmsM+Qb0WobAG9MnZ5AEDldSOBrM
+VyJfEuoF12EPsbOUYjVzJz1swIWgrqZlo1ZKD/oC4Wx0/zXz+5gWWbgXykTWE4wV
+PzU8r33qkgiEtXOjMc5YbvWmTcM0xw7OH34LPOtgUNZtcYSK2u4p1NQ+bDFpXar5
+MEmfmILYFBxGyoe1tCut0M6ulzzV8iBhWHecGEx09Ln3wfoJE+ba0PNn4bdJm6T6
+QQOCAQUAAoIBADPF6xGfYIrIPqiJaeHzTU/q4zpKRCGcjw1chtsNn+oZQzNqvWbI
+XNu7E+MBGimgYerJzyx7lE5bfyu+C4CS6acOutX3ujYfHRVkkkyJedv8q5Ky8kJk
+OjyyhS+cAszbQdh/zvBu6SoDa50mcmk/jfgiRZT0FiSNBJD5nlgjyo2cTEK7e2oR
+GD2N7l43M9BuNjUjQqgeRO9RMt6g4iRO/+KlC/yJrSy/PrLARatk/21ZbCn8jofi
+WR3uNkh7bT7dIfJDDmLsRuQ5fegdQ9mQ/7nLvMZha4pitwTlaI6P0c76fRN1Al27
+6LpcuPd1iHi4UjnvGR5nRwVN68igLNp2tGY=
+-----END PUBLIC KEY-----
diff --git a/include/staff/apikey.inc.php b/include/staff/apikey.inc.php
index 9de6a2f75cb2675ff7c3b75f619f5b4f7e0522dc..1e6f845cf05beb9c4d65710b951346fc239a3fcc 100644
--- a/include/staff/apikey.inc.php
+++ b/include/staff/apikey.inc.php
@@ -1,17 +1,18 @@
 <?php
 if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied');
+
 $info=array();
 $qstr='';
 if($api && $_REQUEST['a']!='add'){
-    $title='Update API Key';
+    $title=__('Update API Key');
     $action='update';
-    $submit_text='Save Changes';
+    $submit_text=__('Save Changes');
     $info=$api->getHashtable();
     $qstr.='&id='.$api->getId();
 }else {
-    $title='Add New API Key';
+    $title=__('Add New API Key');
     $action='add';
-    $submit_text='Add Key';
+    $submit_text=__('Add Key');
     $info['isactive']=isset($info['isactive'])?$info['isactive']:1;
     $qstr.='&a='.urlencode($_REQUEST['a']);
 }
@@ -22,7 +23,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="do" value="<?php echo $action; ?>">
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2>API Key
+ <h2><?php echo __('API Key');?>
     <i class="help-tip icon-question-sign" href="#api_key"></i>
     </h2>
  <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
@@ -30,25 +31,25 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em>API Key is auto-generated. Delete and re-add to change the key.</em>
+                <em><?php echo __('API Key is auto-generated. Delete and re-add to change the key.');?></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <td width="150" class="required">
-                Status:
+                <?php echo __('Status');?>:
             </td>
             <td>
-                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong>Active</strong>
-                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>>Disabled
+                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong><?php echo __('Active');?></strong>
+                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>><?php echo __('Disabled');?>
                 &nbsp;<span class="error">*&nbsp;</span>
             </td>
         </tr>
         <?php if($api){ ?>
         <tr>
             <td width="150">
-                IP Address:
+                <?php echo __('IP Address');?>:
             </td>
             <td>
                 <span>
@@ -59,14 +60,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="150">
-                API Key:
+                <?php echo __('API Key');?>:
             </td>
             <td><?php echo $api->getKey(); ?> &nbsp;</td>
         </tr>
         <?php }else{ ?>
         <tr>
             <td width="150" class="required">
-               IP Address:
+               <?php echo __('IP Address');?>:
             </td>
             <td>
                 <span>
@@ -79,14 +80,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <?php } ?>
         <tr>
             <th colspan="2">
-                <em><strong>Services:</strong>: Check applicable API services enabled for the key.</em>
+                <em><strong><?php echo __('Services');?>:</strong> <?php echo __('Check applicable API services enabled for the key.');?></em>
             </th>
         </tr>
         <tr>
             <td colspan=2 style="padding-left:5px">
                 <label>
                     <input type="checkbox" name="can_create_tickets" value="1" <?php echo $info['can_create_tickets']?'checked="checked"':''; ?> >
-                    Can Create Tickets <em>(XML/JSON/EMAIL)</em>
+                    <?php echo __('Can Create Tickets <em>(XML/JSON/EMAIL)</em>');?>
                 </label>
             </td>
         </tr>
@@ -94,13 +95,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             <td colspan=2 style="padding-left:5px">
                 <label>
                     <input type="checkbox" name="can_exec_cron" value="1" <?php echo $info['can_exec_cron']?'checked="checked"':''; ?> >
-                    Can Execute Cron
+                    <?php echo __('Can Execute Cron');?>
                 </label>
             </td>
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Admin Notes</strong>: Internal notes.&nbsp;</em>
+                <em><strong><?php echo __('Admin Notes');?></strong>: <?php echo __('Internal notes.');?>&nbsp;</em>
             </th>
         </tr>
         <tr>
@@ -113,7 +114,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 </table>
 <p style="padding-left:225px;">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="apikeys.php"'>
+    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="apikeys.php"'>
 </p>
 </form>
diff --git a/include/staff/apikeys.inc.php b/include/staff/apikeys.inc.php
index 7dd0d00003a1416509dcb49220fc764bfd6fc84f..ba45adccef44860eec3ec52136b60950971b9289 100644
--- a/include/staff/apikeys.inc.php
+++ b/include/staff/apikeys.inc.php
@@ -33,17 +33,17 @@ $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
 $query="$sql ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
 $res=db_query($query);
 if($res && ($num=db_num_rows($res)))
-    $showing=$pageNav->showing().' API Keys';
+    $showing=$pageNav->showing().' '.__('API Keys');
 else
-    $showing='No API keys found!';
+    $showing=__('No API keys found!');
 
 ?>
 
-<div style="width:700px;padding-top:5px; float:left;">
- <h2>API Keys</h2>
+<div class="pull-left" style="width:700px;padding-top:5px;">
+ <h2><?php echo __('API Keys');?></h2>
 </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
- <b><a href="apikeys.php?a=add" class="Icon newapi">Add New API Key</a></b></div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
+ <b><a href="apikeys.php?a=add" class="Icon newapi"><?php echo __('Add New API Key');?></a></b></div>
 <div class="clear"></div>
 <form action="apikeys.php" method="POST" name="keys">
  <?php csrf_token(); ?>
@@ -53,12 +53,12 @@ else
     <caption><?php echo $showing; ?></caption>
     <thead>
         <tr>
-            <th width="7">&nbsp;</th>        
-            <th width="320"><a <?php echo $key_sort; ?> href="apikeys.php?<?php echo $qstr; ?>&sort=key">API Key</a></th>
-            <th width="120"><a  <?php echo $ip_sort; ?> href="apikeys.php?<?php echo $qstr; ?>&sort=ip">IP Addr.</a></th>
-            <th width="100"><a  <?php echo $status_sort; ?> href="apikeys.php?<?php echo $qstr; ?>&sort=status">Status</a></th>
-            <th width="150" nowrap><a  <?php echo $date_sort; ?>href="apikeys.php?<?php echo $qstr; ?>&sort=date">Date Added</a></th>
-            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="apikeys.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th>
+            <th width="7">&nbsp;</th>
+            <th width="320"><a <?php echo $key_sort; ?> href="apikeys.php?<?php echo $qstr; ?>&sort=key"><?php echo __('API Key');?></a></th>
+            <th width="120"><a <?php echo $ip_sort; ?> href="apikeys.php?<?php echo $qstr; ?>&sort=ip"><?php echo __('IP Address');?></a></th>
+            <th width="100"><a  <?php echo $status_sort; ?> href="apikeys.php?<?php echo $qstr; ?>&sort=status"><?php echo __('Status');?></a></th>
+            <th width="150" nowrap><a  <?php echo $date_sort; ?>href="apikeys.php?<?php echo $qstr; ?>&sort=date"><?php echo __('Date Added');?></a></th>
+            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="apikeys.php?<?php echo $qstr; ?>&sort=updated"><?php echo __('Last Updated');?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -73,11 +73,11 @@ else
                 ?>
             <tr id="<?php echo $row['id']; ?>">
                 <td width=7px>
-                  <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['id']; ?>" 
+                  <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['id']; ?>"
                             <?php echo $sel?'checked="checked"':''; ?>> </td>
                 <td>&nbsp;<a href="apikeys.php?id=<?php echo $row['id']; ?>"><?php echo Format::htmlchars($row['apikey']); ?></a></td>
                 <td><?php echo $row['ipaddr']; ?></td>
-                <td><?php echo $row['isactive']?'Active':'<b>Disabled</b>'; ?></td>
+                <td><?php echo $row['isactive']?__('Active'):'<b>'.__('Disabled').'</b>'; ?></td>
                 <td>&nbsp;<?php echo Format::db_date($row['created']); ?></td>
                 <td>&nbsp;<?php echo Format::db_datetime($row['updated']); ?></td>
             </tr>
@@ -88,12 +88,12 @@ else
      <tr>
         <td colspan="7">
             <?php if($res && $num){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No API keys found';
+                echo __('No API keys found');
             } ?>
         </td>
      </tr>
@@ -101,39 +101,42 @@ else
 </table>
 <?php
 if($res && $num): //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="enable" value="Enable" >
-    <input class="button" type="submit" name="disable" value="Disable">
-    <input class="button" type="submit" name="delete" value="Delete">
+    <input class="button" type="submit" name="enable" value="<?php echo __('Enable');?>" >
+    <input class="button" type="submit" name="disable" value="<?php echo __('Disable');?>">
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete');?>">
 </p>
 <?php
 endif;
 ?>
 </form>
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="enable-confirm">
-        Are you sure want to <b>enable</b> selected API keys?
+        <?php echo sprintf(__('Are you sure want to <b>enable</b> %s?'),
+            _N('selected API key', 'selected API keys', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="disable-confirm">
-        Are you sure want to <b>disable</b>  selected API keys?
+        <?php echo sprintf(__('Are you sure want to <b>disable</b> %s?'),
+            _N('selected API key', 'selected API keys', 2)); ?>
     </p>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected API keys?</strong></font>
-        <br><br>Deleted keys CANNOT be recovered.
+        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
+            _N('selected API key', 'selected API keys', 2));?></strong></font>
+        <br><br><?php echo __('Deleted data CANNOT be recovered.'); ?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/attachment.inc.php b/include/staff/attachment.inc.php
deleted file mode 100644
index 6027678123ca55684c721ae72f98133135f603f7..0000000000000000000000000000000000000000
--- a/include/staff/attachment.inc.php
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied');
-//Get the config info.
-$config=($errors && $_POST)?Format::input($_POST):$cfg->getConfigInfo();
-?>
-<table width="100%" border="0" cellspacing=0 cellpadding=0>
-    <form action="admin.php?t=attach" method="post">
-    <input type="hidden" name="t" value="attach">
-    <tr>
-      <td>
-        <table width="100%" border="0" cellspacing=0 cellpadding=2 class="tform">
-          <tr class="header">
-            <td colspan=2>&nbsp;Attachments Settings</td>
-          </tr>
-          <tr class="subheader">
-            <td colspan=2">
-                Before enabling attachments make sure you understand the security settings and issues related to file uploads.</td>
-          </tr>
-          <tr>
-            <th width="165">Allow Attachments:</th>
-            <td>
-              <input type="checkbox" name="allow_attachments" <?php echo $config['allow_attachments'] ?'checked':''; ?>><b>Allow Attachments</b>
-                &nbsp; (<i>Global Setting</i>)
-                &nbsp;<font class="error">&nbsp;<?php echo $errors['allow_attachments']; ?></font>
-            </td>
-          </tr>
-          <tr>
-            <th>Emailed Attachments:</th>
-            <td>
-                <input type="checkbox" name="allow_email_attachments" <?php echo $config['allow_email_attachments'] ? 'checked':''; ?> > Accept emailed files
-                    &nbsp;<font class="warn">&nbsp;<?php echo $warn['allow_email_attachments']; ?></font>
-            </td>
-          </tr>
-         <tr>
-            <th>Online Attachments:</th>
-            <td>
-                <input type="checkbox" name="allow_online_attachments" <?php echo $config['allow_online_attachments'] ?'checked':''; ?> >
-                    Allow online attachments upload<br/>&nbsp;&nbsp;&nbsp;&nbsp;
-                <input type="checkbox" name="allow_online_attachments_onlogin" <?php echo $config['allow_online_attachments_onlogin'] ?'checked':''; ?> >
-                    Authenticated users Only. (<i>User must be logged in to upload files </i>)
-                    <font class="warn">&nbsp;<?php echo $warn['allow_online_attachments']; ?></font>
-            </td>
-          </tr>
-          <tr>
-            <th>Staff Response Files:</th>
-            <td>
-                <input type="checkbox" name="email_attachments" <?php echo $config['email_attachments']?'checked':''; ?> >Email attachments to the user
-            </td>
-          </tr>
-          <tr>
-            <th nowrap>Maximum File Size:</th>
-            <td>
-              <input type="text" name="max_file_size" value="<?php echo $config['max_file_size']; ?>"> <i>bytes</i>
-                <font class="error">&nbsp;<?php echo $errors['max_file_size']; ?></font>
-            </td>
-          </tr>
-          <tr>
-            <th>Attachment Folder:</th>
-            <td>
-                Web user (e.g apache) must have write access to the folder. &nbsp;<font class="error">&nbsp;<?php echo $errors['upload_dir']; ?></font><br>
-              <input type="text" size=60 name="upload_dir" value="<?php echo $config['upload_dir']; ?>"> 
-              <font color=red>
-              <?php echo $attwarn; ?>
-              </font>
-            </td>
-          </tr>
-          <tr>
-            <th valign="top"><br/>Accepted File Types:</th>
-            <td>
-                Enter file extensions allowed separated by a comma. e.g <i>.doc, .pdf, </i> <br>
-                To accept all files enter wildcard <b><i>.*</i></b>&nbsp;&nbsp;i.e dotStar (NOT recommended).
-                <textarea name="allowed_filetypes" cols="21" rows="4" style="width: 65%;" wrap=HARD ><?php echo $config['allowed_filetypes']; ?></textarea>
-            </td>
-          </tr>
-        </table>
-    </td></tr>
-    <tr><td style="padding:10px 0 10px 200px">
-        <input class="button" type="submit" name="submit" value="Save Changes">
-        <input class="button" type="reset" name="reset" value="Reset Changes">
-    </td></tr>
-  </form>
-</table>
diff --git a/include/staff/banlist.inc.php b/include/staff/banlist.inc.php
index b059e329683c51456d3b56ec090c39ab902b03ba..26e1d074d2fb070cedbc4242e03ad082cf023c71 100644
--- a/include/staff/banlist.inc.php
+++ b/include/staff/banlist.inc.php
@@ -14,7 +14,7 @@ if($_REQUEST['q'] && strlen($_REQUEST['q'])>3) {
         $where.=' AND rule.val LIKE "%'.db_input($_REQUEST['q'],false).'%"';
 
 }elseif($_REQUEST['q']) {
-    $errors['q']='Term too short!';
+    $errors['q']=__('Term too short!');
 }
 
 $sortOptions=array('email'=>'rule.val','status'=>'isactive','created'=>'rule.created','created'=>'rule.updated');
@@ -46,30 +46,30 @@ $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
 $query="$select $from $where ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
 //echo $query;
 ?>
-<h2>Banned Email Addresses
+<h2><?php echo __('Banned Email Addresses');?>
     <i class="help-tip icon-question-sign" href="#ban_list"></i>
     </h2>
-<div style="width:600; float:left;padding-top:5px;">
+<div class="pull-left" style="width:600;padding-top:5px;">
     <form action="banlist.php" method="GET" name="filter">
      <input type="hidden" name="a" value="filter" >
      <div>
-       Query: <input name="q" type="text" size="20" value="<?php echo Format::htmlchars($_REQUEST['q']); ?>">
+       <?php echo __('Query');?>: <input name="q" type="text" size="20" value="<?php echo Format::htmlchars($_REQUEST['q']); ?>">
         &nbsp;&nbsp;
-        <input type="submit" name="submit" value="Search"/>
+        <input type="submit" name="submit" value="<?php echo __('Search');?>"/>
      </div>
     </form>
  </div>
-<div style="float:right;text-align:right;padding-right:5px;"><b><a href="banlist.php?a=add" class="Icon newstaff">Ban New Email</a></b></div>
+<div class="pull-right flush-right" style="padding-right:5px;"><b><a href="banlist.php?a=add" class="Icon newstaff"><?php echo __('Ban New Email');?></a></b></div>
 <div class="clear"></div>
 <?php
 if(($res=db_query($query)) && ($num=db_num_rows($res)))
     $showing=$pageNav->showing();
 else
-    $showing='No banned emails matching the query found!';
+    $showing=__('No banned emails matching the query found!');
 
 if($search)
-    $showing='Search Results: '.$showing;
-    
+    $showing=__('Search Results').': '.$showing;
+
 ?>
 <form action="banlist.php" method="POST" name="banlist">
  <?php csrf_token(); ?>
@@ -79,11 +79,11 @@ if($search)
     <caption><?php echo $showing; ?></caption>
     <thead>
         <tr>
-            <th width="7px">&nbsp;</th>        
-            <th width="350"><a <?php echo $email_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=email">Email Address</a></th>
-            <th width="200"><a  <?php echo $status_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=status">Ban Status</a></th>
-            <th width="120"><a <?php echo $created_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=created">Date Added</a></th>
-            <th width="120"><a <?php echo $updated_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th>
+            <th width="7px">&nbsp;</th>
+            <th width="350"><a <?php echo $email_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=email"><?php echo __('Email Address');?></a></th>
+            <th width="200"><a  <?php echo $status_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=status"><?php echo __('Ban Status');?></a></th>
+            <th width="120"><a <?php echo $created_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=created"><?php echo __('Date Added');?></a></th>
+            <th width="120"><a <?php echo $updated_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=updated"><?php echo __('Last Updated');?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -100,7 +100,7 @@ if($search)
                   <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['id']; ?>" <?php echo $sel?'checked="checked"':''; ?>>
                 </td>
                 <td>&nbsp;<a href="banlist.php?id=<?php echo $row['id']; ?>"><?php echo Format::htmlchars($row['val']); ?></a></td>
-                <td>&nbsp;&nbsp;<?php echo $row['isactive']?'Active':'<b>Disabled</b>'; ?></td>
+                <td>&nbsp;&nbsp;<?php echo $row['isactive']?__('Active'):'<b>'.__('Disabled').'</b>'; ?></td>
                 <td><?php echo Format::db_date($row['created']); ?></td>
                 <td><?php echo Format::db_datetime($row['updated']); ?>&nbsp;</td>
                </tr>
@@ -111,12 +111,12 @@ if($search)
      <tr>
         <td colspan="5">
             <?php if($res && $num){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No banned emails found!';
+                echo __('No banned emails found!');
             } ?>
         </td>
      </tr>
@@ -124,14 +124,14 @@ if($search)
 </table>
 <?php
 if($res && $num): //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="enable" value="Enable" >
+    <input class="button" type="submit" name="enable" value="<?php echo __('Enable');?>" >
     &nbsp;&nbsp;
-    <input class="button" type="submit" name="disable" value="Disable" >
+    <input class="button" type="submit" name="disable" value="<?php echo __('Disable');?>" >
     &nbsp;&nbsp;
-    <input class="button" type="submit" name="delete" value="Delete">
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete');?>">
 </p>
 <?php
 endif;
@@ -139,26 +139,29 @@ endif;
 </form>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="enable-confirm">
-        Are you sure want to <b>enable</b> selected ban rules?
+        <?php echo sprintf(__('Are you sure want to <b>enable</b> %s?'),
+            _N('selected ban rule', 'selected ban rules', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="disable-confirm">
-        Are you sure want to <b>disable</b>  selected ban rules?
+        <?php echo sprintf(__('Are you sure want to <b>disable</b> %s?'),
+            _N('selected ban rule', 'selected ban rules', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected ban rules?</strong></font>
+        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
+            _N('selected ban rule', 'selected ban rules', 2));?></strong></font>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/banrule.inc.php b/include/staff/banrule.inc.php
index a1c48f2e3f502015d07206782d60e8954b63a1fa..b3fe52347ccc875025eaa05fb490bd3b4a5be7a5 100644
--- a/include/staff/banrule.inc.php
+++ b/include/staff/banrule.inc.php
@@ -4,16 +4,16 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access
 $info=array();
 $qstr='';
 if($rule && $_REQUEST['a']!='add'){
-    $title='Update Ban Rule';
+    $title=__('Update Ban Rule');
     $action='update';
-    $submit_text='Update';
+    $submit_text=__('Update');
     $info=$rule->getInfo();
     $info['id']=$rule->getId();
     $qstr.='&id='.$rule->getId();
 }else {
-    $title='Add New Email Address to Ban List';
+    $title=__('Add New Email Address to Ban List');
     $action='add';
-    $submit_text='Add';
+    $submit_text=__('Add');
     $info['isactive']=isset($info['isactive'])?$info['isactive']:1;
     $qstr.='&a='.urlencode($_REQUEST['a']);
 }
@@ -24,7 +24,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="do" value="<?php echo $action; ?>">
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2>Manage Email Ban Rule
+ <h2><?php echo __('Manage Email Ban Rule');?>
     <i class="help-tip icon-question-sign" href="#ban_list"></i>
     </h2>
  <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
@@ -32,24 +32,24 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em>Valid email address required.</em>
+                <em><?php echo __('Valid email address required');?></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <td width="180" class="required">
-                Ban Status:
+                <?php echo __('Ban Status'); ?>:
             </td>
             <td>
-                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong>Active</strong>
-                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>>Disabled
+                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong><?php echo __('Active');?></strong>
+                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>><?php echo __('Disabled');?>
                 &nbsp;<span class="error">*&nbsp;</span>
             </td>
         </tr>
         <tr>
             <td width="180" class="required">
-                Email Address:
+                <?php echo __('Email Address');?>:
             </td>
             <td>
                 <input name="val" type="text" size="24" value="<?php echo $info['val']; ?>">
@@ -58,7 +58,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Internal notes</strong>: Admin notes&nbsp;</em>
+                <em><strong><?php echo __('Internal notes');?></strong>: <?php echo __('Admin notes');?>&nbsp;</em>
             </th>
         </tr>
         <tr>
@@ -69,9 +69,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
     </tbody>
 </table>
-<p style="padding-left:225px;">
+<p style="text-align:center;">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="banlist.php"'>
+    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="banlist.php"'>
 </p>
 </form>
diff --git a/include/staff/cannedresponse.inc.php b/include/staff/cannedresponse.inc.php
index e7f6d6d87d28c7e6d35a67e083d21ee876559390..6be5a63b1e7b2834971715c939e1e3073af0d78c 100644
--- a/include/staff/cannedresponse.inc.php
+++ b/include/staff/cannedresponse.inc.php
@@ -3,9 +3,9 @@ if(!defined('OSTSCPINC') || !$thisstaff) die('Access Denied');
 $info=array();
 $qstr='';
 if($canned && $_REQUEST['a']!='add'){
-    $title='Update Canned Response';
+    $title=__('Update Canned Response');
     $action='update';
-    $submit_text='Save Changes';
+    $submit_text=__('Save Changes');
     $info=$canned->getInfo();
     $info['id']=$canned->getId();
     $qstr.='&id='.$canned->getId();
@@ -13,9 +13,9 @@ if($canned && $_REQUEST['a']!='add'){
     $info['response'] = $canned->getResponseWithImages();
     $info['notes'] = Format::viewableImages($info['notes']);
 }else {
-    $title='Add New Canned Response';
+    $title=__('Add New Canned Response');
     $action='create';
-    $submit_text='Add Response';
+    $submit_text=__('Add Response');
     $info['isenabled']=isset($info['isenabled'])?$info['isenabled']:1;
     $qstr.='&a='.$_REQUEST['a'];
 }
@@ -27,32 +27,34 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="do" value="<?php echo $action; ?>">
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2>Canned Response&nbsp;<i class="help-tip icon-question-sign" href="#canned_response"></i></h2>
+ <h2><?php echo __('Canned Response')?>
+ &nbsp;<i class="help-tip icon-question-sign" href="#canned_response"></i></h2>
  <table class="form_table fixed" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
         <tr><td></td><td></td></tr> <!-- For fixed table layout -->
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em>Canned response settings</em>
+                <em><?php echo __('Canned response settings');?></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
-            <td width="180" class="required">Status:</td>
+            <td width="180" class="required"><?php echo __('Status');?>:</td>
             <td>
-                <label><input type="radio" name="isenabled" value="1" <?php echo $info['isenabled']?'checked="checked"':''; ?>>&nbsp;Active&nbsp;</label>
-                <label><input type="radio" name="isenabled" value="0" <?php echo !$info['isenabled']?'checked="checked"':''; ?>>&nbsp;Disabled&nbsp;</label>
+                <label><input type="radio" name="isenabled" value="1" <?php
+                    echo $info['isenabled']?'checked="checked"':''; ?>>&nbsp;<?php echo __('Active'); ?>&nbsp;</label>
+                <label><input type="radio" name="isenabled" value="0" <?php
+                        echo !$info['isenabled']?'checked="checked"':''; ?>>&nbsp;<?php echo __('Disabled'); ?>&nbsp;</label>
                 &nbsp;<span class="error">*&nbsp;<?php echo $errors['isenabled']; ?></span>
-                &nbsp;<i class="help-tip icon-question-sign" href="#status"></i>
             </td>
         </tr>
         <tr>
-            <td width="180" class="required">Department:</td>
+            <td width="180" class="required"><?php echo __('Department');?>:</td>
             <td>
                 <select name="dept_id">
-                    <option value="0">&mdash; All Departments &mdash;</option>
+                    <option value="0">&mdash; <?php echo __('All Departments');?> &mdash;</option>
                     <?php
                     $sql='SELECT dept_id, dept_name FROM '.DEPT_TABLE.' dept ORDER by dept_name';
                     if(($res=db_query($sql)) && db_num_rows($res)) {
@@ -68,48 +70,42 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Canned Response</strong>: Make the title short and clear.&nbsp;</em>
+                <em><strong><?php echo __('Canned Response');?></strong>: <?php echo __('Make the title short and clear.');?>&nbsp;</em>
             </th>
         </tr>
         <tr>
             <td colspan=2>
-                <div><b>Title</b><span class="error">*&nbsp;<?php echo $errors['title']; ?></span></div>
+                <div><b><?php echo __('Title');?></b><span class="error">*&nbsp;<?php echo $errors['title']; ?></span></div>
                 <input type="text" size="70" name="title" value="<?php echo $info['title']; ?>">
-                <br><br><div style="margin-bottom:0.5em"><b>Canned Response</b> <font class="error">*&nbsp;<?php echo $errors['response']; ?></font>
-                    &nbsp;&nbsp;&nbsp;(<a class="tip" href="#ticket_variables">Supported Variables</a>)
+                <br><br>
+                <div style="margin-bottom:0.5em"><b><?php echo __('Canned Response'); ?></b>
+                    <font class="error">*&nbsp;<?php echo $errors['response']; ?></font>
+                    &nbsp;&nbsp;&nbsp;(<a class="tip" href="#ticket_variables"><?php echo __('Supported Variables'); ?></a>)
                     </div>
                 <textarea name="response" class="richtext draft draft-delete" cols="21" rows="12"
                     data-draft-namespace="canned"
                     data-draft-object-id="<?php if (isset($canned)) echo $canned->getId(); ?>"
                     style="width:98%;" class="richtext draft"><?php
                         echo $info['response']; ?></textarea>
-                <br><br><div><b>Canned Attachments</b> (optional) &nbsp;<i class="help-tip icon-question-sign" href="#canned_attachments"></i><font class="error">&nbsp;<?php echo $errors['files']; ?></font></div>
-                <?php
-                if($canned && ($files=$canned->attachments->getSeparates())) {
-                    echo '<div id="canned_attachments"><span class="faded">Uncheck to delete the attachment on submit</span><br>';
-                    foreach($files as $file) {
-                        $hash=$file['key'].md5($file['id'].session_id().strtolower($file['key']));
-                        echo sprintf('<label><input type="checkbox" name="files[]" id="f%d" value="%d" checked="checked">
-                                      <a href="file.php?h=%s">%s</a>&nbsp;&nbsp;</label>&nbsp;',
-                                      $file['id'], $file['id'], $hash, $file['name']);
-                    }
-                    echo '</div><br>';
-
-                }
-                //Hardcoded limit... TODO: add a setting on admin panel - what happens on tickets page??
-                if(count($files)<10) {
-                ?>
-                <div>
-                    <input type="file" name="attachments[]" value=""/>
+                <div><h3><?php echo __('Canned Attachments'); ?> <?php echo __('(optional)'); ?>
+                &nbsp;<i class="help-tip icon-question-sign" href="#canned_attachments"></i></h3>
+                <div class="error"><?php echo $errors['files']; ?></div>
                 </div>
                 <?php
-                }?>
-                <div class="faded">You can upload up to 10 attachments per canned response.</div>
+                $attachments = $canned_form->getField('attachments');
+                if ($canned && ($files=$canned->attachments->getSeparates())) {
+                    $ids = array();
+                    foreach ($files as $f)
+                        $ids[] = $f['id'];
+                    $attachments->value = $ids;
+                }
+                print $attachments->render(); ?>
+                <br/>
             </td>
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Internal Notes</strong>: Notes about the canned response.&nbsp;</em>
+                <em><strong><?php echo __('Internal Notes');?></strong>: <?php echo __('Notes about the canned response.');?>&nbsp;</em>
             </th>
         </tr>
         <tr>
@@ -122,15 +118,15 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 </table>
  <?php if ($canned && $canned->getFilters()) { ?>
     <br/>
-    <div id="msg_warning">Canned response is in use by email filter(s): <?php
+    <div id="msg_warning"><?php echo __('Canned response is in use by email filter(s)');?>: <?php
     echo implode(', ', $canned->getFilters()); ?></div>
  <?php } ?>
 <p style="padding-left:225px;">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset" onclick="javascript:
+    <input type="reset"  name="reset"  value="<?php echo __('Reset'); ?>" onclick="javascript:
         $(this.form).find('textarea.richtext')
             .redactor('deleteDraft');
         location.reload();" />
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="canned.php"'>
+    <input type="button" name="cancel" value="<?php echo __('Cancel'); ?>" onclick='window.location.href="canned.php"'>
 </p>
 </form>
diff --git a/include/staff/cannedresponses.inc.php b/include/staff/cannedresponses.inc.php
index 39a8b75ee2b3ee30c17a4ecb01c30dfecf03d6dd..70645010559a65e2aadf5469715945d6498b63c3 100644
--- a/include/staff/cannedresponses.inc.php
+++ b/include/staff/cannedresponses.inc.php
@@ -42,16 +42,17 @@ $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
 $query="$sql GROUP BY canned.canned_id ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
 $res=db_query($query);
 if($res && ($num=db_num_rows($res)))
-    $showing=$pageNav->showing().' premade responses';
+    $showing=$pageNav->showing().' '._N('premade response', 'premade responses',
+        $total);
 else
-    $showing='No premade responses found!';
+    $showing=__('No premade responses found!');
 
 ?>
-<div style="width:700px;padding-top:5px; float:left;">
- <h2>Canned Responses&nbsp;<i class="help-tip icon-question-sign" href="#canned_responses"></i></h2>
+<div class="pull-left" style="width:700px;padding-top:5px;">
+ <h2><?php echo __('Canned Responses');?></h2>
  </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
-    <b><a href="canned.php?a=add" class="Icon newReply">Add New Response</a></b></div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
+    <b><a href="canned.php?a=add" class="Icon newReply"><?php echo __('Add New Response');?></a></b></div>
 <div class="clear"></div>
 <form action="canned.php" method="POST" name="canned">
  <?php csrf_token(); ?>
@@ -62,10 +63,10 @@ else
     <thead>
         <tr>
             <th width="7">&nbsp;</th>
-            <th width="500"><a <?php echo $title_sort; ?> href="canned.php?<?php echo $qstr; ?>&sort=title">Title</a></th>
-            <th width="80"><a  <?php echo $status_sort; ?> href="canned.php?<?php echo $qstr; ?>&sort=status">Status</a></th>
-            <th width="200"><a  <?php echo $dept_sort; ?> href="canned.php?<?php echo $qstr; ?>&sort=dept">Department</a></th>
-            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="canned.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th>
+            <th width="500"><a <?php echo $title_sort; ?> href="canned.php?<?php echo $qstr; ?>&sort=title"><?php echo __('Title');?></a></th>
+            <th width="80"><a  <?php echo $status_sort; ?> href="canned.php?<?php echo $qstr; ?>&sort=status"><?php echo __('Status');?></a></th>
+            <th width="200"><a  <?php echo $dept_sort; ?> href="canned.php?<?php echo $qstr; ?>&sort=dept"><?php echo __('Department');?></a></th>
+            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="canned.php?<?php echo $qstr; ?>&sort=updated"><?php echo __('Last Updated');?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -87,8 +88,8 @@ else
                 <td>
                     <a href="canned.php?id=<?php echo $row['canned_id']; ?>"><?php echo Format::truncate($row['title'],200); echo "&nbsp;$files"; ?></a>&nbsp;
                 </td>
-                <td><?php echo $row['isenabled']?'Active':'<b>Disabled</b>'; ?></td>
-                <td><?php echo $row['department']?$row['department']:'&mdash; All Departments &mdash;'; ?></td>
+                <td><?php echo $row['isenabled']?__('Active'):'<b>'.__('Disabled').'</b>'; ?></td>
+                <td><?php echo $row['department']?$row['department']:'&mdash; '.__('All Departments').' &mdash;'; ?></td>
                 <td>&nbsp;<?php echo Format::db_datetime($row['updated']); ?></td>
             </tr>
             <?php
@@ -98,12 +99,12 @@ else
      <tr>
         <td colspan="5">
             <?php if($res && $num){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No canned responses';
+                echo __('No canned responses');
             } ?>
         </td>
      </tr>
@@ -111,42 +112,42 @@ else
 </table>
 <?php
 if($res && $num): //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="enable" value="Enable" >
-    <input class="button" type="submit" name="disable" value="Disable" >
-    <input class="button" type="submit" name="delete" value="Delete" >
+    <input class="button" type="submit" name="enable" value="<?php echo __('Enable');?>" >
+    <input class="button" type="submit" name="disable" value="<?php echo __('Disable');?>" >
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete');?>" >
 </p>
 <?php
 endif;
 ?>
 </form>
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="enable-confirm">
-        Are you sure want to <b>enable</b> selected canned responses?
+        <?php echo sprintf(__('Are you sure want to <b>enable</b> %s?'),
+            _N('selected canned response', 'selected canned responses', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="disable-confirm">
-        Are you sure want to <b>disable</b> selected canned responses?
-    </p>
-    <p class="confirm-action" style="display:none;" id="mark_overdue-confirm">
-        Are you sure want to flag the selected tickets as <font color="red"><b>overdue</b></font>?
+        <?php echo sprintf(__('Are you sure want to <b>disable</b> %s?'),
+            _N('selected canned response', 'selected canned responses', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected canned responses?</strong></font>
-        <br><br>Deleted items CANNOT be recovered, including any associated attachments.
+        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
+            _N('selected canned response', 'selected canned responses', 2));?></strong></font>
+        <br><br><?php echo __('Deleted data CANNOT be recovered, including any associated attachments.'); ?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/categories.inc.php b/include/staff/categories.inc.php
index 2a139a02796a8ca86d7ecaf99e9292a9906fa8af..a948f4976ca123a275e5c3dc0b663db2788e55d7 100644
--- a/include/staff/categories.inc.php
+++ b/include/staff/categories.inc.php
@@ -35,16 +35,16 @@ $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
 $query="$sql GROUP BY cat.category_id ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
 $res=db_query($query);
 if($res && ($num=db_num_rows($res)))
-    $showing=$pageNav->showing().' categories';
+    $showing=$pageNav->showing().' '.__('categories');
 else
-    $showing='No FAQ categories found!';
+    $showing=__('No FAQ categories found!');
 
 ?>
-<div style="width:700px;padding-top:5px; float:left;">
- <h2>FAQ Categories&nbsp;<i class="help-tip icon-question-sign" href="#faq_categories"></i></h2>
+<div class="pull-left" style="width:700px;padding-top:5px;">
+ <h2><?php echo __('FAQ Categories');?></h2>
  </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
-    <b><a href="categories.php?a=add" class="Icon newCategory">Add New Category</a></b></div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
+    <b><a href="categories.php?a=add" class="Icon newCategory"><?php echo __('Add New Category');?></a></b></div>
 <div class="clear"></div>
 <form action="categories.php" method="POST" name="cat">
  <?php csrf_token(); ?>
@@ -55,10 +55,10 @@ else
     <thead>
         <tr>
             <th width="7">&nbsp;</th>
-            <th width="500"><a <?php echo $name_sort; ?> href="categories.php?<?php echo $qstr; ?>&sort=name">Name</a></th>
-            <th width="150"><a  <?php echo $type_sort; ?> href="categories.php?<?php echo $qstr; ?>&sort=type">Type</a></th>
-            <th width="80"><a  <?php echo $faqs_sort; ?> href="categories.php?<?php echo $qstr; ?>&sort=faqs">FAQs</a></th>
-            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="categories.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th>
+            <th width="500"><a <?php echo $name_sort; ?> href="categories.php?<?php echo $qstr; ?>&sort=name"><?php echo __('Name');?></a></th>
+            <th width="150"><a  <?php echo $type_sort; ?> href="categories.php?<?php echo $qstr; ?>&sort=type"><?php echo __('Type');?></a></th>
+            <th width="80"><a  <?php echo $faqs_sort; ?> href="categories.php?<?php echo $qstr; ?>&sort=faqs"><?php echo __('FAQs');?></a></th>
+            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="categories.php?<?php echo $qstr; ?>&sort=updated"><?php echo __('Last Updated');?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -70,7 +70,7 @@ else
                 $sel=false;
                 if($ids && in_array($row['category_id'],$ids))
                     $sel=true;
-                
+
                 $faqs=0;
                 if($row['faqs'])
                     $faqs=sprintf('<a href="faq.php?cid=%d">%d</a>',$row['category_id'],$row['faqs']);
@@ -81,7 +81,7 @@ else
                             <?php echo $sel?'checked="checked"':''; ?>>
                 </td>
                 <td><a href="categories.php?id=<?php echo $row['category_id']; ?>"><?php echo Format::truncate($row['name'],200); ?></a>&nbsp;</td>
-                <td><?php echo $row['ispublic']?'<b>Public</b>':'Internal'; ?></td>
+                <td><?php echo $row['ispublic']?'<b>'.__('Public').'</b>':__('Internal'); ?></td>
                 <td style="text-align:right;padding-right:25px;"><?php echo $faqs; ?></td>
                 <td>&nbsp;<?php echo Format::db_datetime($row['updated']); ?></td>
             </tr>
@@ -92,12 +92,12 @@ else
      <tr>
         <td colspan="5">
             <?php if($res && $num){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No FAQ categories found.';
+                echo __('No FAQ categories found.');
             } ?>
         </td>
      </tr>
@@ -105,39 +105,42 @@ else
 </table>
 <?php
 if($res && $num): //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="make_public" value="Make Public">
-    <input class="button" type="submit" name="make_private" value="Make Internal">
-    <input class="button" type="submit" name="delete" value="Delete" >
+    <input class="button" type="submit" name="make_public" value="<?php echo __('Make Public');?>">
+    <input class="button" type="submit" name="make_private" value="<?php echo __('Make Internal');?>">
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete');?>" >
 </p>
 <?php
 endif;
 ?>
 </form>
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="make_public-confirm">
-        Are you sure want to make selected categories <b>public</b>?
+        <?php echo sprintf(__('Are you sure want to make %s <b>public</b>?'),
+            _N('selected category', 'selected categories', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="make_private-confirm">
-        Are you sure want to make selected categories <b>private</b> (internal)?
+        <?php echo sprintf(__('Are you sure want to make %s <b>private</b> (internal)?'),
+            _N('selected category', 'selected categories', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected categories?</strong></font>
-        <br><br>Deleted entries CANNOT be recovered, including any associated FAQs.
+        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
+            _N('selected category', 'selected categories', 2));?></strong></font>
+        <br><br><?php echo __('Deleted data CANNOT be recovered, including any associated FAQs.'); ?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/category.inc.php b/include/staff/category.inc.php
index a12d52b79902206dec18b5080213e63a365d6f5e..ff1ef1f93e558c31c96dd43e800a300519bf333f 100644
--- a/include/staff/category.inc.php
+++ b/include/staff/category.inc.php
@@ -3,17 +3,17 @@ if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->canManageFAQ()) die('Acc
 $info=array();
 $qstr='';
 if($category && $_REQUEST['a']!='add'){
-    $title='Update Category: '.$category->getName();
+    $title=__('Update Category').': '.$category->getName();
     $action='update';
-    $submit_text='Save Changes';
+    $submit_text=__('Save Changes');
     $info=$category->getHashtable();
     $info['id']=$category->getId();
     $info['notes'] = Format::viewableImages($category->getNotes());
     $qstr.='&id='.$category->getId();
 }else {
-    $title='Add New Category';
+    $title=__('Add New Category');
     $action='create';
-    $submit_text='Add';
+    $submit_text=__('Add');
     $qstr.='&a='.$_REQUEST['a'];
 }
 $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
@@ -24,7 +24,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="do" value="<?php echo $action; ?>">
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2>FAQ Category</h2>
+ <h2><?php echo __('FAQ Category');?></h2>
  <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
         <tr>
@@ -36,26 +36,27 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
     <tbody>
         <tr>
             <th colspan="2">
-                <em>Category information&nbsp;<i class="help-tip icon-question-sign" href="#category_information"></i></em>
+                <em><?php echo __('Category information'); ?>
+                <i class="help-tip icon-question-sign" href="#category_information"></i></em>
             </th>
         </tr>
         <tr>
-            <td width="180" class="required">Category Type:</td>
+            <td width="180" class="required"><?php echo __('Category Type');?>:</td>
             <td>
-                <input type="radio" name="ispublic" value="1" <?php echo $info['ispublic']?'checked="checked"':''; ?>><b>Public</b> (publish)
+                <input type="radio" name="ispublic" value="1" <?php echo $info['ispublic']?'checked="checked"':''; ?>><b><?php echo __('Public');?></b> <?php echo __('(publish)');?>
                 &nbsp;&nbsp;&nbsp;&nbsp;
-                <input type="radio" name="ispublic" value="0" <?php echo !$info['ispublic']?'checked="checked"':''; ?>>Private (internal)
+                <input type="radio" name="ispublic" value="0" <?php echo !$info['ispublic']?'checked="checked"':''; ?>><?php echo __('Private');?> <?php echo __('(internal)');?>
                 &nbsp;<span class="error">*&nbsp;<?php echo $errors['ispublic']; ?></span>
             </td>
         </tr>
         <tr>
             <td colspan=2>
-                <div style="padding-top:3px;"><b>Category Name</b>:&nbsp;<span class="faded">Short descriptive name.</span></div>
+                <div style="padding-top:3px;"><b><?php echo __('Category Name');?></b>:&nbsp;<span class="faded"><?php echo __('Short descriptive name.');?></span></div>
                     <input type="text" size="70" name="name" value="<?php echo $info['name']; ?>">
                     &nbsp;<span class="error">*&nbsp;<?php echo $errors['name']; ?></span>
                 <br>
                 <div style="padding-top:5px;">
-                    <b>Category Description</b>:&nbsp;<span class="faded">Summary of the category.</span>
+                    <b><?php echo __('Category Description');?></b>:&nbsp;<span class="faded"><?php echo __('Summary of the category.');?></span>
                     &nbsp;
                     <font class="error">*&nbsp;<?php echo $errors['description']; ?></font></div>
                     <textarea class="richtext" name="description" cols="21" rows="12" style="width:98%;"><?php echo $info['description']; ?></textarea>
@@ -63,7 +64,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <th colspan="2">
-                <em>Internal Notes&nbsp;</em>
+                <em><?php echo __('Internal Notes');?>&nbsp;</em>
             </th>
         </tr>
         <tr>
@@ -76,7 +77,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 </table>
 <p style="padding-left:225px;">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="categories.php"'>
+    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="categories.php"'>
 </p>
 </form>
diff --git a/include/staff/department.inc.php b/include/staff/department.inc.php
index f6ecc995543ed3338aba0858b9082e5f38fb1e75..53d30908c9b324be7e0a1ce3db0554cc15e0c30f 100644
--- a/include/staff/department.inc.php
+++ b/include/staff/department.inc.php
@@ -4,18 +4,18 @@ $info=array();
 $qstr='';
 if($dept && $_REQUEST['a']!='add') {
     //Editing Department.
-    $title='Update Department';
+    $title=__('Update Department');
     $action='update';
-    $submit_text='Save Changes';
+    $submit_text=__('Save Changes');
     $info=$dept->getInfo();
     $info['id']=$dept->getId();
     $info['groups'] = $dept->getAllowedGroups();
 
     $qstr.='&id='.$dept->getId();
 } else {
-    $title='Add New Department';
+    $title=__('Add New Department');
     $action='create';
-    $submit_text='Create Dept';
+    $submit_text=__('Create Dept');
     $info['ispublic']=isset($info['ispublic'])?$info['ispublic']:1;
     $info['ticket_auto_response']=isset($info['ticket_auto_response'])?$info['ticket_auto_response']:1;
     $info['message_auto_response']=isset($info['message_auto_response'])?$info['message_auto_response']:1;
@@ -31,20 +31,20 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="do" value="<?php echo $action; ?>">
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2>Department</h2>
+ <h2><?php echo __('Department');?></h2>
  <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em>Department Information</em>
+                <em><?php echo __('Department Information');?></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <td width="180" class="required">
-                Name:
+                <?php echo __('Name');?>:
             </td>
             <td>
                 <input type="text" size="30" name="name" value="<?php echo $info['name']; ?>">
@@ -53,22 +53,22 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180" class="required">
-                Type:
+                <?php echo __('Type');?>:
             </td>
             <td>
-                <input type="radio" name="ispublic" value="1" <?php echo $info['ispublic']?'checked="checked"':''; ?>><strong>Public</strong>
+                <input type="radio" name="ispublic" value="1" <?php echo $info['ispublic']?'checked="checked"':''; ?>><strong><?php echo __('Public');?></strong>
                 &nbsp;
-                <input type="radio" name="ispublic" value="0" <?php echo !$info['ispublic']?'checked="checked"':''; ?>><strong>Private</strong> (Internal)
+                <input type="radio" name="ispublic" value="0" <?php echo !$info['ispublic']?'checked="checked"':''; ?>><strong><?php echo __('Private');?></strong> <?php echo mb_convert_case(__('(internal)'), MB_CASE_TITLE);?>
                 &nbsp;<i class="help-tip icon-question-sign" href="#type"></i>
             </td>
         </tr>
         <tr>
             <td width="180">
-                SLA:
+                <?php echo __('SLA'); ?>:
             </td>
             <td>
                 <select name="sla_id">
-                    <option value="0">&mdash; System Default &mdash;</option>
+                    <option value="0">&mdash; <?php echo __('System Default'); ?> &mdash;</option>
                     <?php
                     if($slas=SLA::getSLAs()) {
                         foreach($slas as $id =>$name) {
@@ -83,12 +83,12 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-                Manager:
+                <?php echo __('Manager'); ?>:
             </td>
             <td>
                 <span>
                 <select name="manager_id">
-                    <option value="0">&mdash; None &mdash;</option>
+                    <option value="0">&mdash; <?php echo __('None'); ?> &mdash;</option>
                     <?php
                     $sql='SELECT staff_id,CONCAT_WS(", ",lastname, firstname) as name '
                         .' FROM '.STAFF_TABLE.' staff '
@@ -107,28 +107,28 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             </td>
         </tr>
         <tr>
-            <td>Ticket Assignment:</td>
+            <td><?php echo __('Ticket Assignment'); ?>:</td>
             <td>
                 <span>
                 <input type="checkbox" name="assign_members_only" <?php echo
                 $info['assign_members_only']?'checked="checked"':''; ?>>
-                Restrict ticket assignment to department members
+                <?php echo __('Restrict ticket assignment to department members'); ?>
                 <i class="help-tip icon-question-sign" href="#sandboxing"></i>
                 </span>
             </td>
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Outgoing Email Settings</strong>:</em>
+                <em><strong><?php echo __('Outgoing Email Settings'); ?></strong>:</em>
             </th>
         </tr>
         <tr>
             <td width="180">
-                Outgoing Email:
+                <?php echo __('Outgoing Email'); ?>:
             </td>
             <td>
                 <select name="email_id">
-                    <option value="0">&mdash; System Default &mdash;</option>
+                    <option value="0">&mdash; <?php echo __('System Default'); ?> &mdash;</option>
                     <?php
                     $sql='SELECT email_id,email,name FROM '.EMAIL_TABLE.' email ORDER by name';
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -146,11 +146,11 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-                Template Set:
+                <?php echo __('Template Set'); ?>:
             </td>
             <td>
                 <select name="tpl_id">
-                    <option value="0">&mdash; System Default &mdash;</option>
+                    <option value="0">&mdash; <?php echo __('System Default'); ?> &mdash;</option>
                     <?php
                     $sql='SELECT tpl_id,name FROM '.EMAIL_TEMPLATE_GRP_TABLE.' tpl WHERE isactive=1 ORDER by name';
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -166,41 +166,43 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Autoresponder Settings</strong>:
+                <em><strong><?php echo __('Autoresponder Settings'); ?></strong>:
                 <i class="help-tip icon-question-sign" href="#auto_response_settings"></i></em>
             </th>
         </tr>
         <tr>
             <td width="180">
-                New Ticket:
+                <?php echo __('New Ticket');?>:
             </td>
             <td>
                 <span>
                 <input type="checkbox" name="ticket_auto_response" value="0" <?php echo !$info['ticket_auto_response']?'checked="checked"':''; ?> >
 
-                <strong>Disable</strong> for this Department&nbsp;<i class="help-tip icon-question-sign" href="#new_ticket"></i>
+                <?php echo __('<strong>Disable</strong> for this Department'); ?>
+                <i class="help-tip icon-question-sign" href="#new_ticket"></i>
                 </span>
             </td>
         </tr>
         <tr>
             <td width="180">
-                New Message:
+                <?php echo __('New Message');?>:
             </td>
             <td>
                 <span>
                 <input type="checkbox" name="message_auto_response" value="0" <?php echo !$info['message_auto_response']?'checked="checked"':''; ?> >
-                    <strong>Disable</strong> for this Department&nbsp;<i class="help-tip icon-question-sign" href="#new_message"></i>
+                <?php echo __('<strong>Disable</strong> for this Department'); ?>
+                <i class="help-tip icon-question-sign" href="#new_message"></i>
                 </span>
             </td>
         </tr>
         <tr>
             <td width="180">
-                Auto-Response Email:
+                <?php echo __('Auto-Response Email'); ?>:
             </td>
             <td>
                 <span>
                 <select name="autoresp_email_id">
-                    <option value="0" selected="selected">&mdash; Department Email &mdash;</option>
+                    <option value="0" selected="selected">&mdash; <?php echo __('Department Email'); ?> &mdash;</option>
                     <?php
                     $sql='SELECT email_id,email,name FROM '.EMAIL_TABLE.' email ORDER by name';
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -222,20 +224,21 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Alerts &amp; Notices:</strong>&nbsp;<i class="help-tip icon-question-sign" href="#group_membership"></i></em>
+                <em><strong><?php echo __('Alerts and Notices'); ?>:</strong>
+                <i class="help-tip icon-question-sign" href="#group_membership"></i></em>
             </th>
         </tr>
         <tr>
             <td width="180">
-                Recipients:
+                <?php echo __('Recipients'); ?>:
             </td>
             <td>
                 <span>
                 <select name="group_membership">
 <?php foreach (array(
-    Dept::ALERTS_DISABLED =>        "No one (disable Alerts &amp; Notices)",
-    Dept::ALERTS_DEPT_ONLY =>       "Department members only",
-    Dept::ALERTS_DEPT_AND_GROUPS => "Department and Group members",
+    Dept::ALERTS_DISABLED =>        __("No one (disable Alerts and Notices)"),
+    Dept::ALERTS_DEPT_ONLY =>       __("Department members only"),
+    Dept::ALERTS_DEPT_AND_GROUPS => __("Department and Group members"),
 ) as $mode=>$desc) { ?>
     <option value="<?php echo $mode; ?>" <?php
         if ($info['group_membership'] == $mode) echo 'selected="selected"';
@@ -248,7 +251,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Group Access</strong>: Check all groups allowed to access this department.&nbsp;<i class="help-tip icon-question-sign" href="#department_access"></i></em>
+                <em><strong><?php echo __('Group Access'); ?></strong>:
+                <?php echo __('Check all groups allowed to access this department.'); ?>
+                <i class="help-tip icon-question-sign" href="#department_access"></i></em>
             </th>
         </tr>
         <?php
@@ -270,7 +275,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         ?>
         <tr>
             <th colspan="2">
-                <em><strong>Department Signature</strong>:&nbsp;<span class="error">&nbsp;<?php echo $errors['signature']; ?></span>&nbsp;<i class="help-tip icon-question-sign" href="#department_signature"></i></em>
+                <em><strong><?php echo __('Department Signature'); ?></strong>:
+                <span class="error">&nbsp;<?php echo $errors['signature']; ?></span>
+                <i class="help-tip icon-question-sign" href="#department_signature"></i></em>
             </th>
         </tr>
         <tr>
@@ -283,7 +290,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 </table>
 <p style="text-align:center">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="departments.php"'>
+    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="departments.php"'>
 </p>
 </form>
diff --git a/include/staff/departments.inc.php b/include/staff/departments.inc.php
index da9c74c5e3e3c3e3e38060e352eb4700b4812f34..ba2d04707bc23d6c699951a048ad75d6fe07dcc1 100644
--- a/include/staff/departments.inc.php
+++ b/include/staff/departments.inc.php
@@ -35,16 +35,17 @@ $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
 $query="$sql GROUP BY dept.dept_id ORDER BY $order_by";
 $res=db_query($query);
 if($res && ($num=db_num_rows($res)))
-    $showing="Showing 1-$num of $num departments";
+    $showing=sprintf(_N('Showing %d department', 'Showing %d departments',
+        $num),$num);
 else
-    $showing='No departments found!';
+    $showing=__('No departments found!');
 
 ?>
-<div style="width:700px;padding-top:5px; float:left;">
- <h2>Departments</h2>
+<div class="pull-left" style="width:700px;padding-top:5px;">
+ <h2><?php echo __('Departments');?></h2>
  </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
-    <b><a href="departments.php?a=add" class="Icon newDepartment">Add New Department</a></b></div>
+<div class="pull-left flush-right" style="padding-top:5px;padding-right:5px;">
+    <b><a href="departments.php?a=add" class="Icon newDepartment"><?php echo __('Add New Department');?></a></b></div>
 <div class="clear"></div>
 <form action="departments.php" method="POST" name="depts">
  <?php csrf_token(); ?>
@@ -55,11 +56,11 @@ else
     <thead>
         <tr>
             <th width="7px">&nbsp;</th>
-            <th width="180"><a <?php echo $name_sort; ?> href="departments.php?<?php echo $qstr; ?>&sort=name">Name</a></th>
-            <th width="80"><a  <?php echo $type_sort; ?> href="departments.php?<?php echo $qstr; ?>&sort=type">Type</a></th>
-            <th width="70"><a  <?php echo $users_sort; ?>href="departments.php?<?php echo $qstr; ?>&sort=users">Users</a></th>
-            <th width="300"><a  <?php echo $email_sort; ?> href="departments.php?<?php echo $qstr; ?>&sort=email">Email Address</a></th>
-            <th width="200"><a  <?php echo $manager_sort; ?> href="departments.php?<?php echo $qstr; ?>&sort=manager">Dept. Manager</a></th>
+            <th width="180"><a <?php echo $name_sort; ?> href="departments.php?<?php echo $qstr; ?>&sort=name"><?php echo __('Name');?></a></th>
+            <th width="80"><a  <?php echo $type_sort; ?> href="departments.php?<?php echo $qstr; ?>&sort=type"><?php echo __('Type');?></a></th>
+            <th width="70"><a  <?php echo $users_sort; ?>href="departments.php?<?php echo $qstr; ?>&sort=users"><?php echo __('Users');?></a></th>
+            <th width="300"><a  <?php echo $email_sort; ?> href="departments.php?<?php echo $qstr; ?>&sort=email"><?php echo __('Email Address');?></a></th>
+            <th width="200"><a  <?php echo $manager_sort; ?> href="departments.php?<?php echo $qstr; ?>&sort=manager"><?php echo __('Department Manager');?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -82,7 +83,7 @@ else
                     $row['email'] = $defaultEmailAddress;
                 }
 
-                $default=($defaultId==$row['dept_id'])?' <small>(Default)</small>':'';
+                $default=($defaultId==$row['dept_id'])?' <small>'.__('(Default)').'</small>':'';
                 ?>
             <tr id="<?php echo $row['dept_id']; ?>">
                 <td width=7px>
@@ -90,7 +91,7 @@ else
                             <?php echo $sel?'checked="checked"':''; ?>  <?php echo $default?'disabled="disabled"':''; ?> >
                 </td>
                 <td><a href="departments.php?id=<?php echo $row['dept_id']; ?>"><?php echo $row['dept_name']; ?></a>&nbsp;<?php echo $default; ?></td>
-                <td><?php echo $row['ispublic']?'Public':'<b>Private</b>'; ?></td>
+                <td><?php echo $row['ispublic']?__('Public'):'<b>'.__('Private').'</b>'; ?></td>
                 <td>&nbsp;&nbsp;
                     <b>
                     <?php if($row['users']>0) { ?>
@@ -99,8 +100,8 @@ else
                     <?php } ?>
                     </b>
                 </td>
-                <td><a href="emails.php?id=<?php echo $row['email_id']; ?>"><?php
-                    echo Format::htmlchars($row['email']); ?></a>&nbsp;</td>
+                <td><span class="ltr"><a href="emails.php?id=<?php echo $row['email_id']; ?>"><?php
+                    echo Format::htmlchars($row['email']); ?></a></span></td>
                 <td><a href="staff.php?id=<?php echo $row['manager_id']; ?>"><?php echo $row['manager']; ?>&nbsp;</a></td>
             </tr>
             <?php
@@ -110,12 +111,12 @@ else
      <tr>
         <td colspan="6">
             <?php if($res && $num){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No department found';
+                echo __('No department found');
             } ?>
         </td>
      </tr>
@@ -125,9 +126,9 @@ else
 if($res && $num): //Show options..
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="make_public" value="Make Public" >
-    <input class="button" type="submit" name="make_private" value="Make Private" >
-    <input class="button" type="submit" name="delete" value="Delete Dept(s)" >
+    <input class="button" type="submit" name="make_public" value="<?php echo __('Make Public');?>" >
+    <input class="button" type="submit" name="make_private" value="<?php echo __('Make Private');?>" >
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete Dept(s)');?>" >
 </p>
 <?php
 endif;
@@ -135,27 +136,30 @@ endif;
 </form>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="make_public-confirm">
-        Are you sure want to make selected departments <b>public</b>?
+        <?php echo sprintf(__('Are you sure want to make %s <b>public</b>?'),
+            _N('selected department', 'selected departments', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="make_private-confirm">
-        Are you sure want to make selected departments <b>private</b>?
+        <?php echo sprintf(__('Are you sure want to make %s <b>private</b> (internal)?'),
+            _N('selected department', 'selected departments', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected departments?</strong></font>
-        <br><br>Deleted departments CANNOT be recovered.
+        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
+            _N('selected department', 'selected departments', 2));?></strong></font>
+        <br><br><?php echo __('Deleted data CANNOT be recovered.'); ?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/directory.inc.php b/include/staff/directory.inc.php
index 35b0137fbc5f8654a911d797357db401b79d803d..6d127962a2f9d30a9b5ed655333f170cac453ce9 100644
--- a/include/staff/directory.inc.php
+++ b/include/staff/directory.inc.php
@@ -60,12 +60,13 @@ $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
 $query="$select $from $where GROUP BY staff.staff_id ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
 //echo $query;
 ?>
-<h2>Staff Members&nbsp;<i class="help-tip icon-question-sign" href="#staff_members"></i></h2>
-<div style="width:700px; float:left;">
+<h2><?php echo __('Agents');?>
+&nbsp;<i class="help-tip icon-question-sign" href="#staff_members"></i></h2>
+<div class="pull-left" style="width:700px">
     <form action="directory.php" method="GET" name="filter">
        <input type="text" name="q" value="<?php echo Format::htmlchars($_REQUEST['q']); ?>" >
         <select name="did" id="did">
-             <option value="0">&mdash; All Departments &mdash;</option>
+             <option value="0">&mdash; <?php echo __('All Departments');?> &mdash;</option>
              <?php
              $sql='SELECT dept.dept_id, dept.dept_name,count(staff.staff_id) as users  '.
                   'FROM '.DEPT_TABLE.' dept '.
@@ -80,27 +81,28 @@ $query="$select $from $where GROUP BY staff.staff_id ORDER BY $order_by LIMIT ".
              ?>
         </select>
         &nbsp;&nbsp;
-        <input type="submit" name="submit" value="Filter"/>&nbsp;&nbsp;<i class="help-tip icon-question-sign" href="#apply_filtering_criteria"></i>
+        <input type="submit" name="submit" value="<?php echo __('Filter');?>"/>
+        &nbsp;<i class="help-tip icon-question-sign" href="#apply_filtering_criteria"></i>
     </form>
  </div>
 <div class="clear"></div>
 <?php
 $res=db_query($query);
-if($res && ($num=db_num_rows($res)))        
+if($res && ($num=db_num_rows($res)))
     $showing=$pageNav->showing();
 else
-    $showing='No staff members found!';
+    $showing=__('No agents found!');
 ?>
 <table class="list" border="0" cellspacing="1" cellpadding="0" width="940">
     <caption><?php echo $showing; ?></caption>
     <thead>
         <tr>
-            <th width="160"><a <?php echo $name_sort; ?> href="directory.php?<?php echo $qstr; ?>&sort=name">Name</a></th>
-            <th width="150"><a  <?php echo $dept_sort; ?>href="directory.php?<?php echo $qstr; ?>&sort=dept">Department</a></th>
-            <th width="180"><a  <?php echo $email_sort; ?>href="directory.php?<?php echo $qstr; ?>&sort=email">Email Address</a></th>
-            <th width="120"><a <?php echo $phone_sort; ?> href="directory.php?<?php echo $qstr; ?>&sort=phone">Phone Number</a></th>
-            <th width="80"><a <?php echo $ext_sort; ?> href="directory.php?<?php echo $qstr; ?>&sort=ext">Phone Ext</a></th>
-            <th width="120"><a <?php echo $mobile_sort; ?> href="directory.php?<?php echo $qstr; ?>&sort=mobile">Mobile Number</a></th>
+            <th width="160"><a <?php echo $name_sort; ?> href="directory.php?<?php echo $qstr; ?>&sort=name"><?php echo __('Name');?></a></th>
+            <th width="150"><a  <?php echo $dept_sort; ?>href="directory.php?<?php echo $qstr; ?>&sort=dept"><?php echo __('Department');?></a></th>
+            <th width="180"><a  <?php echo $email_sort; ?>href="directory.php?<?php echo $qstr; ?>&sort=email"><?php echo __('Email Address');?></a></th>
+            <th width="120"><a <?php echo $phone_sort; ?> href="directory.php?<?php echo $qstr; ?>&sort=phone"><?php echo __('Phone Number');?></a></th>
+            <th width="80"><a <?php echo $ext_sort; ?> href="directory.php?<?php echo $qstr; ?>&sort=ext"><?php echo __(/* As in a phone number `extension` */ 'Extension');?></a></th>
+            <th width="120"><a <?php echo $mobile_sort; ?> href="directory.php?<?php echo $qstr; ?>&sort=mobile"><?php echo __('Mobile Number');?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -123,10 +125,10 @@ else
      <tr>
         <td colspan="6">
             <?php if($res && $num) {
-                echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+                echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
                 ?>
             <?php } else {
-                echo 'No staff members found!';
+                echo __('No agents found!');
             } ?>
         </td>
      </tr>
diff --git a/include/staff/dynamic-form.inc.php b/include/staff/dynamic-form.inc.php
index 5f9905c40807faad2d30df2d49a1d0d0c1f5744c..4b1b852a09eedad808c779ba32e58c6d6b4c4bec 100644
--- a/include/staff/dynamic-form.inc.php
+++ b/include/staff/dynamic-form.inc.php
@@ -2,17 +2,17 @@
 
 $info=array();
 if($form && $_REQUEST['a']!='add') {
-    $title = 'Update custom form section';
+    $title = __('Update custom form section');
     $action = 'update';
     $url = "?id=".urlencode($_REQUEST['id']);
-    $submit_text='Save Changes';
+    $submit_text=__('Save Changes');
     $info = $form->ht;
     $newcount=2;
 } else {
-    $title = 'Add new custom form section';
+    $title = __('Add new custom form section');
     $action = 'add';
     $url = '?a=add';
-    $submit_text='Add Form';
+    $submit_text=__('Add Form');
     $newcount=4;
 }
 $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
@@ -23,20 +23,21 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
     <input type="hidden" name="do" value="<?php echo $action; ?>">
     <input type="hidden" name="a" value="<?php echo $action; ?>">
     <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
-    <h2>Custom Form</h2>
+    <h2><?php echo __('Custom Form'); ?></h2>
     <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em>Custom forms are used to allow custom data to be
-                associated with tickets</em>
+                <em><?php echo __(
+                'Custom forms are used to allow custom data to be associated with tickets'
+                ); ?></em>
             </th>
         </tr>
     </thead>
     <tbody style="vertical-align:top">
         <tr>
-            <td width="180" class="required">Title:</td>
+            <td width="180" class="required"><?php echo __('Title'); ?>:</td>
             <td><input type="text" name="title" size="40" value="<?php
                 echo $info['title']; ?>"/>
                 <i class="help-tip icon-question-sign" href="#form_title"></i>
@@ -45,7 +46,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             </td>
         </tr>
         <tr>
-            <td width="180">Instructions:</td>
+            <td width="180"><?php echo __('Instructions'); ?>:</td>
             <td><textarea name="instructions" rows="3" cols="40"><?php
                 echo $info['instructions']; ?></textarea>
                 <i class="help-tip icon-question-sign" href="#form_instructions"></i>
@@ -58,17 +59,19 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
     <thead>
         <tr>
             <th colspan="7">
-                <em><strong>User Information Fields</strong> more information here</em>
+                <em><strong><?php echo __('User Information Fields'); ?></strong>
+                <?php echo sprintf(__('(These fields are requested for new tickets
+                via the %s form)'),
+                UserForm::objects()->one()->get('title')); ?></em>
             </th>
         </tr>
         <tr>
             <th></th>
-            <th>Label</th>
-            <th>Type</th>
-            <th>Internal</th>
-            <th>Required</th>
-            <th>Variable</th>
-            <th>Delete</th>
+            <th><?php echo __('Label'); ?></th>
+            <th><?php echo __('Type'); ?></th>
+            <th><?php echo __('Visibility'); ?></th>
+            <th><?php echo __('Variable'); ?></th>
+            <th><?php echo __('Delete'); ?></th>
         </tr>
     </thead>
     <tbody>
@@ -81,10 +84,12 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <tr>
             <td></td>
             <td><?php echo $f->get('label'); ?></td>
-            <td><?php $t=FormField::getFieldType($f->get('type')); echo $t[0]; ?></td>
-            <td><input type="checkbox" disabled="disabled"/></td>
-            <td><input type="checkbox" disabled="disabled"
-                <?php echo $f->get('required') ? 'checked="checked"' : ''; ?>/></td>
+            <td><?php $t=FormField::getFieldType($f->get('type')); echo __($t[0]); ?></td>
+            <td><?php
+                $rmode = $f->getRequirementMode();
+                $modes = $f->getAllRequirementModes();
+                echo $modes[$rmode]['desc'];
+            ?></td>
             <td><?php echo $f->get('name'); ?></td>
             <td><input type="checkbox" disabled="disabled"/></td></tr>
 
@@ -94,23 +99,22 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
     <thead>
         <tr>
             <th colspan="7">
-                <em><strong>Form Fields</strong> fields available for ticket information</em>
+                <em><strong><?php echo __('Form Fields'); ?></strong>
+                <?php echo __('fields available where this form is used'); ?></em>
             </th>
         </tr>
         <tr>
-            <th nowrap>Sort
-                <i class="help-tip icon-question-sign" href="#field_sort"></i></th>
-            <th nowrap>Label
+            <th nowrap
+                ><i class="help-tip icon-question-sign" href="#field_sort"></i></th>
+            <th nowrap><?php echo __('Label'); ?>
                 <i class="help-tip icon-question-sign" href="#field_label"></i></th>
-            <th nowrap>Type
+            <th nowrap><?php echo __('Type'); ?>
                 <i class="help-tip icon-question-sign" href="#field_type"></i></th>
-            <th nowrap>Internal
-                <i class="help-tip icon-question-sign" href="#field_internal"></i></th>
-            <th nowrap>Required
-                <i class="help-tip icon-question-sign" href="#field_required"></i></th>
-            <th nowrap>Variable
+            <th nowrap><?php echo __('Visibility'); ?>
+                <i class="help-tip icon-question-sign" href="#field_visibility"></i></th>
+            <th nowrap><?php echo __('Variable'); ?>
                 <i class="help-tip icon-question-sign" href="#field_variable"></i></th>
-            <th nowrap>Delete
+            <th nowrap><?php echo __('Delete'); ?>
                 <i class="help-tip icon-question-sign" href="#field_delete"></i></th>
         </tr>
     </thead>
@@ -119,8 +123,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         $id = $f->get('id');
         $deletable = !$f->isDeletable() ? 'disabled="disabled"' : '';
         $force_name = $f->isNameForced() ? 'disabled="disabled"' : '';
-        $force_privacy = $f->isPrivacyForced() ? 'disabled="disabled"' : '';
-        $force_required = $f->isRequirementForced() ? 'disabled="disabled"' : '';
+        $rmode = $f->getRequirementMode();
         $fi = $f->getImpl();
         $ferrors = $f->errors(); ?>
         <tr>
@@ -130,41 +133,36 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                 <font class="error"><?php
                     if ($ferrors['label']) echo '<br/>'; echo $ferrors['label']; ?>
             </td>
-            <td nowrap><select name="type-<?php echo $id; ?>" <?php
+            <td nowrap><select style="max-width:150px" name="type-<?php echo $id; ?>" <?php
                 if (!$fi->isChangeable()) echo 'disabled="disabled"'; ?>>
                 <?php foreach (FormField::allTypes() as $group=>$types) {
-                        ?><optgroup label="<?php echo Format::htmlchars($group); ?>"><?php
+                        ?><optgroup label="<?php echo Format::htmlchars(__($group)); ?>"><?php
                         foreach ($types as $type=>$nfo) {
                             if ($f->get('type') != $type
                                     && isset($nfo[2]) && !$nfo[2]) continue; ?>
                 <option value="<?php echo $type; ?>" <?php
                     if ($f->get('type') == $type) echo 'selected="selected"'; ?>>
-                    <?php echo $nfo[0]; ?></option>
+                    <?php echo __($nfo[0]); ?></option>
                     <?php } ?>
                 </optgroup>
                 <?php } ?>
             </select>
             <?php if ($f->isConfigurable()) { ?>
-                <a class="action-button" style="float:none;overflow:inherit"
+                <a class="action-button field-config" style="overflow:inherit"
                     href="#ajax.php/form/field-config/<?php
                         echo $f->get('id'); ?>"
                     onclick="javascript:
-                        $('#overlay').show();
-                        $('#field-config .body').load($(this).attr('href').substr(1));
-                        $('#field-config').show();
+                        $.dialog($(this).attr('href').substr(1), [201]);
                         return false;
-                    "><i class="icon-edit"></i> Config</a>
-            <?php } ?>
-            <div class="error" style="white-space:normal"><?php
-                if ($ferrors['type']) echo $ferrors['type'];
-            ?></div>
-            </td>
-            <td><input type="checkbox" name="private-<?php echo $id; ?>"
-                <?php if ($f->get('private')) echo 'checked="checked"'; ?>
-                <?php echo $force_privacy ?>/></td>
-            <td><input type="checkbox" name="required-<?php echo $id; ?>"
-                <?php if ($f->get('required')) echo 'checked="checked"'; ?>
-                <?php echo $force_required ?>/>
+                    "><i class="icon-edit"></i> <?php echo __('Config'); ?></a>
+            <?php } ?></td>
+            <td>
+                <select name="visibility-<?php echo $id; ?>">
+<?php foreach ($f->getAllRequirementModes() as $m=>$I) { ?>
+    <option value="<?php echo $m; ?>" <?php if ($rmode == $m)
+         echo 'selected="selected"'; ?>><?php echo $I['desc']; ?></option>
+<?php } ?>
+                <select>
             </td>
             <td>
                 <input type="text" size="20" name="name-<?php echo $id; ?>"
@@ -190,25 +188,28 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                     value="<?php echo $info["sort-new-$i"]; ?>"/></td>
             <td><input type="text" size="32" name="label-new-<?php echo $i; ?>"
                 value="<?php echo $info["label-new-$i"]; ?>"/></td>
-            <td><select name="type-new-<?php echo $i; ?>">
+            <td><select style="max-width:150px" name="type-new-<?php echo $i; ?>">
                 <?php foreach (FormField::allTypes() as $group=>$types) {
-                    ?><optgroup label="<?php echo Format::htmlchars($group); ?>"><?php
+                    ?><optgroup label="<?php echo Format::htmlchars(__($group)); ?>"><?php
                     foreach ($types as $type=>$nfo) {
                         if (isset($nfo[2]) && !$nfo[2]) continue; ?>
                 <option value="<?php echo $type; ?>"
                     <?php if ($info["type-new-$i"] == $type) echo 'selected="selected"'; ?>>
-                    <?php echo $nfo[0]; ?>
+                    <?php echo __($nfo[0]); ?>
                 </option>
                     <?php } ?>
                 </optgroup>
                 <?php } ?>
             </select></td>
-            <td><input type="checkbox" name="private-new-<?php echo $i; ?>"
-            <?php if ($info["private-new-$i"]
-                || (!$_POST && $form && $form->get('type') == 'U'))
-                    echo 'checked="checked"'; ?>/></td>
-            <td><input type="checkbox" name="required-new-<?php echo $i; ?>"
-                <?php if ($info["required-new-$i"]) echo 'checked="checked"'; ?>/></td>
+            <td>
+                <select name="visibility-new-<?php echo $i; ?>">
+<?php
+    $rmode = $info['visibility-new-'.$i];
+    foreach (DynamicFormField::allRequirementModes() as $m=>$I) { ?>
+    <option value="<?php echo $m; ?>" <?php if ($rmode == $m)
+         echo 'selected="selected"'; ?>><?php echo $I['desc']; ?></option>
+<?php } ?>
+                <select>
             <td><input type="text" size="20" name="name-new-<?php echo $i; ?>"
                 value="<?php echo $info["name-new-$i"]; ?>"/>
                 <font class="error"><?php
@@ -221,7 +222,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
     <tbody>
         <tr>
             <th colspan="7">
-                <em><strong>Internal Notes:</strong> be liberal, they're internal</em>
+                <em><strong><?php echo __('Internal Notes'); ?>:</strong>
+                <?php echo __("be liberal, they're internal"); ?></em>
             </th>
         </tr>
         <tr>
@@ -234,31 +236,30 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
     </table>
 <p class="centered">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="?"'>
+    <input type="reset"  name="reset"  value="<?php echo __('Reset'); ?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel'); ?>" onclick='window.location.href="?"'>
 </p>
 
 <div style="display:none;" class="draggable dialog" id="delete-confirm">
-    <h3><i class="icon-trash"></i> Remove Existing Data?</h3>
+    <h3><i class="icon-trash"></i> <?php echo __('Remove Existing Data?'); ?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p>
-        <strong>You are about to delete <span id="deleted-count"></span> fields.</strong>
-        Would you also like to remove data currently entered for this field?
-        <em>If you opt not to remove the data now, you will have the option
-        to delete the the data when editing it</em>
+    <strong><?php echo sprintf(__('You are about to delete %s fields.'),
+        '<span id="deleted-count"></span>'); ?></strong>
+        <?php echo __('Would you also like to remove data currently entered for this field? <em> If you opt not to remove the data now, you will have the option to delete the the data when editing it.</em>'); ?>
     </p><p style="color:red">
-        Deleted data CANNOT be recovered.
+        <?php echo __('Deleted data CANNOT be recovered.'); ?>
     </p>
     <hr>
     <div id="deleted-fields"></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel'); ?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="submit" value="Continue" class="confirm">
+        <span class="buttons pull-right">
+            <input type="submit" value="<?php echo __('Continue'); ?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
@@ -266,6 +267,10 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 </form>
 
 <div style="display:none;" class="dialog draggable" id="field-config">
+    <div id="popup-loading">
+        <h1><i class="icon-spinner icon-spin icon-large"></i>
+        <?php echo __('Loading ...');?></h1>
+    </div>
     <div class="body"></div>
 </div>
 
@@ -281,7 +286,8 @@ $('form.manage-form').on('submit.inline', function(e) {
                 .append($('<input/>').attr({type:'checkbox',name:'delete-data-'
                     + $(e).data('fieldId')})
                 ).append($('<strong>').html(
-                    'Remove all data entered for <u>' + $(e).data('fieldLabel') + '</u>'
+                    ' <?php echo __('Remove all data entered for <u> %s </u>?');
+                        ?>'.replace('%s', $(e).data('fieldLabel'))
                 ))
             );
         });
diff --git a/include/staff/dynamic-forms.inc.php b/include/staff/dynamic-forms.inc.php
index 9709d0cec199a07b8050db7c7b48e34b506f47a0..bfa399490f858aebbc46bdf25354d010be718a2e 100644
--- a/include/staff/dynamic-forms.inc.php
+++ b/include/staff/dynamic-forms.inc.php
@@ -1,8 +1,9 @@
-<div style="width:700;padding-top:5px; float:left;">
- <h2>Custom Forms</h2>
+<div class="pull-left" style="width:700;padding-top:5px;">
+ <h2><?php echo __('Custom Forms'); ?></h2>
 </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
- <b><a href="forms.php?a=add" class="Icon form-add">Add New Custom Form</a></b></div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
+<b><a href="forms.php?a=add" class="Icon form-add"><?php
+    echo __('Add New Custom Form'); ?></a></b></div>
 <div class="clear"></div>
 
 <?php
@@ -10,7 +11,7 @@ $page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1;
 $count = DynamicForm::objects()->filter(array('type__in'=>array('G')))->count();
 $pageNav = new Pagenate($count, $page, PAGE_LIMIT);
 $pageNav->setURL('forms.php');
-$showing=$pageNav->showing().' forms';
+$showing=$pageNav->showing().' '._N('form','forms',$count);
 ?>
 
 <form action="forms.php" method="POST" name="forms">
@@ -21,8 +22,8 @@ $showing=$pageNav->showing().' forms';
     <thead>
         <tr>
             <th width="7">&nbsp;</th>
-            <th>Built-in Forms</th>
-            <th>Last Updated</th>
+            <th><?php echo __('Built-in Forms'); ?></th>
+            <th><?php echo __('Last Updated'); ?></th>
         </tr>
     </thead>
     <tbody>
@@ -49,8 +50,8 @@ $showing=$pageNav->showing().' forms';
     <thead>
         <tr>
             <th width="7">&nbsp;</th>
-            <th>Custom Forms</th>
-            <th>Last Updated</th>
+            <th><?php echo __('Custom Forms'); ?></th>
+            <th><?php echo __('Last Updated'); ?></th>
         </tr>
     </thead>
     <tbody>
@@ -76,12 +77,14 @@ $showing=$pageNav->showing().' forms';
      <tr>
         <td colspan="3">
             <?php if($count){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select'); ?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All'); ?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None'); ?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle'); ?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No extra forms defined yet &mdash; <a href="forms.php?a=add">add one!</a>';
+                echo sprintf(__(
+                    'No extra forms defined yet &mdash; %s add one! %s'),
+                    '<a href="forms.php?a=add">','</a>');
             } ?>
         </td>
      </tr>
@@ -89,28 +92,30 @@ $showing=$pageNav->showing().' forms';
 </table>
 <?php
 if ($count) //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="delete" value="Delete">
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete'); ?>">
 </p>
 </form>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm'); ?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected forms?</strong></font>
-        <br><br>Deleted forms CANNOT be recovered.
+        <font color="red"><strong><?php echo sprintf(__(
+        'Are you sure you want to DELETE %s?'),
+        _N('selected custom form', 'selected custom forms', 2));?></strong></font>
+        <br><br><?php echo __('Deleted data CANNOT be recovered.'); ?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.'); ?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
+        <span class="buttons pull-left">
             <input type="button" value="No, Cancel" class="close">
         </span>
-        <span class="buttons" style="float:right">
+        <span class="buttons pull-right">
             <input type="button" value="Yes, Do it!" class="confirm">
         </span>
      </p>
diff --git a/include/staff/dynamic-list.inc.php b/include/staff/dynamic-list.inc.php
index 32b8921e0707c4bd741f89efe33584c05af468f8..f42abef7c27a9b035c5d4927147219bed01a5ad4 100644
--- a/include/staff/dynamic-list.inc.php
+++ b/include/staff/dynamic-list.inc.php
@@ -1,35 +1,37 @@
 <?php
 
 $info=array();
-if($list && !$errors) {
-    $title = 'Update custom list';
+if ($list) {
+    $title = __('Update custom list');
     $action = 'update';
-    $submit_text='Save Changes';
-    $info = $list->ht;
+    $submit_text = __('Save Changes');
+    $info = $list->getInfo();
     $newcount=2;
 } else {
-    $title = 'Add new custom list';
+    $title = __('Add new custom list');
     $action = 'add';
-    $submit_text='Add List';
+    $submit_text = __('Add List');
     $newcount=4;
 }
-$info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
+
+$info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info);
 
 ?>
-<form action="?" method="post" id="save">
+<form action="" method="post" id="save">
     <?php csrf_token(); ?>
     <input type="hidden" name="do" value="<?php echo $action; ?>">
     <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
     <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
-    <h2>Custom List</h2>
+    <h2><?php echo __('Custom List'); ?>
+    <?php echo $list ? $list->getName() : 'Add new list'; ?></h2>
 
 <ul class="tabs">
     <li><a href="#definition" class="active">
-        <i class="icon-plus"></i> Definition</a></li>
+        <i class="icon-plus"></i> <?php echo __('Definition'); ?></a></li>
     <li><a href="#items">
-        <i class="icon-list"></i> Items</a></li>
+        <i class="icon-list"></i> <?php echo __('Items'); ?></a></li>
     <li><a href="#properties">
-        <i class="icon-asterisk"></i> Properties</a></li>
+        <i class="icon-asterisk"></i> <?php echo __('Properties'); ?></a></li>
 </ul>
 
 <div id="definition" class="tab_content">
@@ -38,24 +40,47 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em>Custom lists are used to provide drop-down lists for custom forms. &nbsp;<i class="help-tip icon-question-sign" href="#custom_lists"></i></em>
+                <em><?php echo __(
+                'Custom lists are used to provide drop-down lists for custom forms.'
+                ); ?>&nbsp;<i class="help-tip icon-question-sign" href="#custom_lists"></i></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
-            <td width="180" class="required">Name:</td>
-            <td><input size="50" type="text" name="name" value="<?php echo $info['name']; ?>"/>
-            <span class="error">*<br/><?php echo $errors['name']; ?></td>
+            <td width="180" class="required"><?php echo __('Name'); ?>:</td>
+            <td>
+                <?php
+                if ($list && !$list->isEditable())
+                    echo $list->getName();
+                else {
+                    echo sprintf('<input size="50" type="text" name="name"
+                            value="%s"/> <span
+                            class="error">*<br/>%s</span>',
+                            $info['name'], $errors['name']);
+                }
+                ?>
+            </td>
         </tr>
         <tr>
-            <td width="180">Plural Name:</td>
-            <td><input size="50" type="text" name="name_plural" value="<?php echo $info['name_plural']; ?>"/></td>
+            <td width="180"><?php echo __('Plural Name'); ?>:</td>
+            <td>
+                <?php
+                    if ($list && !$list->isEditable())
+                        echo $list->getPluralName();
+                    else
+                        echo sprintf('<input size="50" type="text"
+                                name="name_plural" value="%s"/>',
+                                $info['name_plural']);
+                ?>
+            </td>
         </tr>
         <tr>
-            <td width="180">Sort Order:</td>
+            <td width="180"><?php echo __('Sort Order'); ?>:</td>
             <td><select name="sort_mode">
-                <?php foreach (DynamicList::getSortModes() as $key=>$desc) { ?>
+                <?php
+                $sortModes = $list ? $list->getSortModes() : DynamicList::getSortModes();
+                foreach ($sortModes as $key=>$desc) { ?>
                 <option value="<?php echo $key; ?>" <?php
                     if ($key == $info['sort_mode']) echo 'selected="selected"';
                     ?>><?php echo $desc; ?></option>
@@ -66,7 +91,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
     <tbody>
         <tr>
             <th colspan="7">
-                <em><strong>Internal Notes:</strong> be liberal, they're internal</em>
+                <em><strong><?php echo __('Internal Notes'); ?>:</strong>
+                <?php echo __("be liberal, they're internal"); ?></em>
             </th>
         </tr>
         <tr>
@@ -83,19 +109,20 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
     <thead>
         <tr>
             <th colspan="7">
-                <em><strong>Item Properties</strong> properties definable for each item</em>
+                <em><strong><?php echo __('Item Properties'); ?></strong>
+                <?php echo __('properties definable for each item'); ?></em>
             </th>
         </tr>
         <tr>
-            <th nowrap>Sort</th>
-            <th nowrap>Label</th>
-            <th nowrap>Type</th>
-            <th nowrap>Variable</th>
-            <th nowrap>Delete</th>
+            <th nowrap></th>
+            <th nowrap><?php echo __('Label'); ?></th>
+            <th nowrap><?php echo __('Type'); ?></th>
+            <th nowrap><?php echo __('Variable'); ?></th>
+            <th nowrap><?php echo __('Delete'); ?></th>
         </tr>
     </thead>
     <tbody class="sortable-rows" data-sort="prop-sort-">
-    <?php if ($form) foreach ($form->getDynamicFields() as $f) {
+    <?php if ($list && $form=$list->getForm()) foreach ($form->getDynamicFields() as $f) {
         $id = $f->get('id');
         $deletable = !$f->isDeletable() ? 'disabled="disabled"' : '';
         $force_name = $f->isNameForced() ? 'disabled="disabled"' : '';
@@ -108,31 +135,26 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                 <font class="error"><?php
                     if ($ferrors['label']) echo '<br/>'; echo $ferrors['label']; ?>
             </td>
-            <td nowrap><select name="type-<?php echo $id; ?>" <?php
-                if (!$fi->isChangeable()) echo 'disabled="disabled"'; ?>>
+            <td nowrap><select style="max-width:150px" name="type-<?php echo $id; ?>" <?php
+                if (!$fi->isChangeable() || !$f->isChangeable()) echo 'disabled="disabled"'; ?>>
                 <?php foreach (FormField::allTypes() as $group=>$types) {
-                        ?><optgroup label="<?php echo Format::htmlchars($group); ?>"><?php
+                        ?><optgroup label="<?php echo Format::htmlchars(__($group)); ?>"><?php
                         foreach ($types as $type=>$nfo) {
                             if ($f->get('type') != $type
                                     && isset($nfo[2]) && !$nfo[2]) continue; ?>
                 <option value="<?php echo $type; ?>" <?php
                     if ($f->get('type') == $type) echo 'selected="selected"'; ?>>
-                    <?php echo $nfo[0]; ?></option>
+                    <?php echo __($nfo[0]); ?></option>
                     <?php } ?>
                 </optgroup>
                 <?php } ?>
             </select>
             <?php if ($f->isConfigurable()) { ?>
-                <a class="action-button" style="float:none;overflow:inherit"
-                    href="#ajax.php/form/field-config/<?php
-                        echo $f->get('id'); ?>"
-                    onclick="javascript:
-                        $('#overlay').show();
-                        $('#field-config .body').load($(this).attr('href').substr(1));
-                        $('#field-config').show();
-                        return false;
-                    "><i class="icon-edit"></i> Config</a>
-            <?php } ?></td>
+                <a class="action-button field-config"
+                    style="overflow:inherit"
+                    href="#form/field-config/<?php
+                        echo $f->get('id'); ?>"><i
+                        class="icon-cog"></i> <?php echo __('Config'); ?></a> <?php } ?></td>
             <td>
                 <input type="text" size="20" name="name-<?php echo $id; ?>"
                     value="<?php echo Format::htmlchars($f->get('name'));
@@ -141,8 +163,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                     if ($ferrors['name']) echo '<br/>'; echo $ferrors['name'];
                 ?></font>
                 </td>
-            <td><input type="checkbox" name="delete-<?php echo $id; ?>"
-                    <?php echo $deletable; ?>/>
+            <td>
+                <?php
+                if (!$f->isDeletable())
+                    echo '<i class="icon-ban-circle"></i>';
+                else
+                    echo sprintf('<input type="checkbox" name="delete-prop-%s">', $id);
+                ?>
                 <input type="hidden" name="prop-sort-<?php echo $id; ?>"
                     value="<?php echo $f->get('sort'); ?>"/>
                 </td>
@@ -155,14 +182,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                     value="<?php echo $info["prop-sort-new-$i"]; ?>"/></td>
             <td><input type="text" size="32" name="prop-label-new-<?php echo $i; ?>"
                 value="<?php echo $info["prop-label-new-$i"]; ?>"/></td>
-            <td><select name="type-new-<?php echo $i; ?>">
+            <td><select style="max-width:150px" name="type-new-<?php echo $i; ?>">
                 <?php foreach (FormField::allTypes() as $group=>$types) {
-                    ?><optgroup label="<?php echo Format::htmlchars($group); ?>"><?php
+                    ?><optgroup label="<?php echo Format::htmlchars(__($group)); ?>"><?php
                     foreach ($types as $type=>$nfo) {
                         if (isset($nfo[2]) && !$nfo[2]) continue; ?>
                 <option value="<?php echo $type; ?>"
                     <?php if ($info["type-new-$i"] == $type) echo 'selected="selected"'; ?>>
-                    <?php echo $nfo[0]; ?>
+                    <?php echo __($nfo[0]); ?>
                 </option>
                     <?php } ?>
                 </optgroup>
@@ -184,13 +211,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
     <thead>
     <?php if ($list) {
         $page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1;
-        $count = $list->getItemCount();
+        $count = $list->getNumItems();
         $pageNav = new Pagenate($count, $page, PAGE_LIMIT);
-        $pageNav->setURL('dynamic-list.php', 'id='.urlencode($_REQUEST['id']));
-        $showing=$pageNav->showing().' list items';
+        $pageNav->setURL('list.php', 'id='.urlencode($list->getId()));
+        $showing=$pageNav->showing().' '.__('list items');
         ?>
     <?php }
-        else $showing = 'Add a few initial items to the list';
+        else $showing = __('Add a few initial items to the list');
     ?>
         <tr>
             <th colspan="5">
@@ -199,68 +226,123 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <th></th>
-            <th>Value</th>
-            <th>Extra <em style="display:inline">&mdash; abbreviations and such</em></th>
-            <th>Disabled</th>
-            <th>Delete</th>
+            <th><?php echo __('Value'); ?></th>
+            <?php
+            if (!$list || $list->hasAbbrev()) { ?>
+            <th><?php echo __(/* Short for 'abbreviation' */ 'Abbrev'); ?> <em style="display:inline">&mdash;
+                <?php echo __('abbreviations and such'); ?></em></th>
+            <?php
+            } ?>
+            <th><?php echo __('Disabled'); ?></th>
+            <th><?php echo __('Delete'); ?></th>
         </tr>
     </thead>
 
     <tbody <?php if ($info['sort_mode'] == 'SortCol') { ?>
             class="sortable-rows" data-sort="sort-"<?php } ?>>
-        <?php if ($list)
-        $icon = ($info['sort_mode'] == 'SortCol')
-            ? '<i class="icon-sort"></i>&nbsp;' : '';
+        <?php
         if ($list) {
+            $icon = ($info['sort_mode'] == 'SortCol')
+                ? '<i class="icon-sort"></i>&nbsp;' : '';
         foreach ($list->getAllItems() as $i) {
-            $id = $i->get('id'); ?>
+            $id = $i->getId(); ?>
         <tr class="<?php if (!$i->isEnabled()) echo 'disabled'; ?>">
             <td><?php echo $icon; ?>
                 <input type="hidden" name="sort-<?php echo $id; ?>"
-                value="<?php echo $i->get('sort'); ?>"/></td>
+                value="<?php echo $i->getSortOrder(); ?>"/></td>
             <td><input type="text" size="40" name="value-<?php echo $id; ?>"
-                value="<?php echo $i->get('value'); ?>"/>
-                <?php if ($form && $form->getFields()) { ?>
-                <a class="action-button" style="float:none;overflow:inherit"
-                    href="#ajax.php/list/item/<?php
-                        echo $i->get('id'); ?>/properties"
-                    onclick="javascript:
-                        $('#overlay').show();
-                        $('#field-config .body').load($(this).attr('href').substr(1));
-                        $('#field-config').show();
-                        return false;
-                    "><i class="icon-edit"></i> Properties</a>
-                <?php } ?></td>
-            <td><input type="text" size="30" name="extra-<?php echo $id; ?>"
-                value="<?php echo $i->get('extra'); ?>"/></td>
+                value="<?php echo $i->getValue(); ?>"/>
+                <?php if ($list->hasProperties()) { ?>
+                   <a class="action-button field-config"
+                       style="overflow:inherit"
+                       href="#list/<?php
+                        echo $list->getId(); ?>/item/<?php
+                        echo $id ?>/properties"
+                       id="item-<?php echo $id; ?>"
+                    ><?php
+                        echo sprintf('<i class="icon-edit" %s></i> ',
+                                $i->getConfiguration()
+                                ? '': 'style="color:red; font-weight:bold;"');
+                        echo __('Properties');
+                   ?></a>
+                <?php
+                }
+
+                if ($errors["value-$id"])
+                    echo sprintf('<br><span class="error">%s</span>',
+                            $errors["value-$id"]);
+                ?>
+            </td>
+            <?php
+            if ($list->hasAbbrev()) { ?>
+            <td><input type="text" size="30" name="abbrev-<?php echo $id; ?>"
+                value="<?php echo $i->getAbbrev(); ?>"/></td>
+            <?php
+            } ?>
             <td>
-                <input type="checkbox" name="disable-<?php echo $id; ?>" <?php
-                if (!$i->isEnabled()) echo 'checked="checked"'; ?>/></td>
+                <?php
+                if (!$i->isDisableable())
+                     echo '<i class="icon-ban-circle"></i>';
+                else
+                    echo sprintf('<input type="checkbox" name="disable-%s"
+                            %s %s />',
+                            $id,
+                            !$i->isEnabled() ? ' checked="checked" ' : '',
+                            (!$i->isEnabled() && !$i->isEnableable()) ? ' disabled="disabled" ' : ''
+                            );
+                ?>
+            </td>
             <td>
-                <input type="checkbox" name="delete-<?php echo $id; ?>"/></td>
+                <?php
+                if (!$i->isDeletable())
+                    echo '<i class="icon-ban-circle"></i>';
+                else
+                    echo sprintf('<input type="checkbox" name="delete-item-%s">', $id);
+
+                ?>
+            </td>
         </tr>
     <?php }
     }
-    for ($i=0; $i<$newcount; $i++) { ?>
+
+    if (!$list || $list->allowAdd()) {
+       for ($i=0; $i<$newcount; $i++) { ?>
         <tr>
             <td><?php echo $icon; ?> <em>+</em>
                 <input type="hidden" name="sort-new-<?php echo $i; ?>"/></td>
             <td><input type="text" size="40" name="value-new-<?php echo $i; ?>"/></td>
-            <td><input type="text" size="30" name="extra-new-<?php echo $i; ?>"/></td>
-            <td></td>
-            <td></td>
+            <?php
+            if (!$list || $list->hasAbbrev()) { ?>
+            <td><input type="text" size="30" name="abbrev-new-<?php echo $i; ?>"/></td>
+            <?php
+            } ?>
+            <td>&nbsp;</td>
+            <td>&nbsp;</td>
         </tr>
-    <?php } ?>
+    <?php
+       }
+    }?>
     </tbody>
     </table>
 </div>
 <p class="centered">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="?"'>
+    <input type="reset"  name="reset"  value="<?php echo __('Reset'); ?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel'); ?>"
+        onclick='window.location.href="?"'>
 </p>
 </form>
 
-<div style="display:none;" class="dialog draggable" id="field-config">
-    <div class="body"></div>
-</div>
+<script type="text/javascript">
+$(function() {
+    $('a.field-config').click( function(e) {
+        e.preventDefault();
+        var $id = $(this).attr('id');
+        var url = 'ajax.php/'+$(this).attr('href').substr(1);
+        $.dialog(url, [201], function (xhr) {
+            $('a#'+$id+' i').removeAttr('style');
+        });
+        return false;
+    });
+});
+</script>
diff --git a/include/staff/dynamic-lists.inc.php b/include/staff/dynamic-lists.inc.php
index 3ef22874d6103bfefd76bc60f97eb3a31c0d8e11..6d294a587fca5c19e3cf66441ac331de7eb7467b 100644
--- a/include/staff/dynamic-lists.inc.php
+++ b/include/staff/dynamic-lists.inc.php
@@ -1,8 +1,9 @@
-<div style="width:700;padding-top:5px; float:left;">
- <h2>Custom Lists</h2>
+<div class="pull-left" style="width:700;padding-top:5px;">
+ <h2><?php echo __('Custom Lists'); ?></h2>
 </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
- <b><a href="lists.php?a=add" class="Icon list-add">Add New Custom List</a></b></div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
+ <b><a href="lists.php?a=add" class="Icon list-add"><?php
+ echo __('Add New Custom List'); ?></a></b></div>
 <div class="clear"></div>
 
 <?php
@@ -10,25 +11,25 @@ $page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1;
 $count = DynamicList::objects()->count();
 $pageNav = new Pagenate($count, $page, PAGE_LIMIT);
 $pageNav->setURL('lists.php');
-$showing=$pageNav->showing().' custom lists';
-?>
+$showing=$pageNav->showing().' '._N('custom list', 'custom lists', $count);
 
+?>
 <form action="lists.php" method="POST" name="lists">
 <?php csrf_token(); ?>
 <input type="hidden" name="do" value="mass_process" >
 <input type="hidden" id="action" name="a" value="" >
 <table class="list" border="0" cellspacing="1" cellpadding="0" width="940">
-    <caption><?php echo $showing; ?></caption>
+    <caption>Custom Lists</caption>
     <thead>
         <tr>
             <th width="7">&nbsp;</th>
-            <th>List Name</th>
-            <th>Created</th>
-            <th>Last Updated</th>
+            <th><?php echo __('List Name'); ?></th>
+            <th><?php echo __('Created') ?></th>
+            <th><?php echo __('Last Updated'); ?></th>
         </tr>
     </thead>
     <tbody>
-    <?php foreach (DynamicList::objects()->order_by('name')
+    <?php foreach (DynamicList::objects()->order_by('-type', 'name')
                 ->limit($pageNav->getLimit())
                 ->offset($pageNav->getStart()) as $list) {
             $sel = false;
@@ -36,9 +37,19 @@ $showing=$pageNav->showing().' custom lists';
                 $sel = true; ?>
         <tr>
             <td>
-                <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $list->get('id'); ?>"
-                    <?php echo $sel?'checked="checked"':''; ?>></td>
-            <td><a href="?id=<?php echo $list->get('id'); ?>"><?php echo $list->get('name'); ?></a></td>
+                <?php
+                if ($list->isDeleteable()) { ?>
+                <input width="7" type="checkbox" class="ckb" name="ids[]"
+                value="<?php echo $list->getId(); ?>"
+                    <?php echo $sel?'checked="checked"':''; ?>>
+                <?php
+                } else {
+                    echo '&nbsp;';
+                }
+                ?>
+            </td>
+            <td><a href="?id=<?php echo $list->getId(); ?>"><?php echo
+            $list->getPluralName() ?: $list->getName(); ?></a></td>
             <td><?php echo $list->get('created'); ?></td>
             <td><?php echo $list->get('updated'); ?></td>
         </tr>
@@ -49,12 +60,13 @@ $showing=$pageNav->showing().' custom lists';
      <tr>
         <td colspan="4">
             <?php if($count){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select'); ?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All'); ?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None'); ?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle'); ?></a>&nbsp;&nbsp;
             <?php } else {
-                echo 'No custom lists defined yet &mdash; add one!';
+                echo sprintf(__('No custom lists defined yet &mdash; %s add one %s!'),
+                    '<a href="lists.php?a=add">','</a>');
             } ?>
         </td>
      </tr>
@@ -62,29 +74,31 @@ $showing=$pageNav->showing().' custom lists';
 </table>
 <?php
 if ($count) //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 ?>
 
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="delete" value="Delete">
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete'); ?>">
 </p>
 </form>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm'); ?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected lists?</strong></font>
-        <br><br>Deleted forms CANNOT be recovered.
+        <font color="red"><strong><?php echo sprintf(
+        __('Are you sure you want to DELETE %s?'),
+        _N('selected custom list', 'selected custom lists', 2)); ?></strong></font>
+        <br><br><?php echo __('Deleted data CANNOT be recovered.'); ?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.'); ?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
+        <span class="buttons pull-left">
             <input type="button" value="No, Cancel" class="close">
         </span>
-        <span class="buttons" style="float:right">
+        <span class="buttons pull-right">
             <input type="button" value="Yes, Do it!" class="confirm">
         </span>
     </p>
diff --git a/include/staff/email.inc.php b/include/staff/email.inc.php
index 8f8e683121a80ca852c4b65073c899bbbbe658a2..9d2e3f89c566b28d2df5eae293b548de28732bf0 100644
--- a/include/staff/email.inc.php
+++ b/include/staff/email.inc.php
@@ -3,9 +3,9 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access
 $info=array();
 $qstr='';
 if($email && $_REQUEST['a']!='add'){
-    $title='Update Email';
+    $title=__('Update Email');
     $action='update';
-    $submit_text='Save Changes';
+    $submit_text=__('Save Changes');
     $info=$email->getInfo();
     $info['id']=$email->getId();
     if($info['mail_delete'])
@@ -15,13 +15,13 @@ if($email && $_REQUEST['a']!='add'){
     else
         $info['postfetch']=''; //nothing.
     if($info['userpass'])
-        $passwdtxt='To change password enter new password above.';
+        $passwdtxt=__('To change password enter new password above.');
 
     $qstr.='&id='.$email->getId();
 }else {
-    $title='Add New Email';
+    $title=__('Add New Email');
     $action='create';
-    $submit_text='Submit';
+    $submit_text=__('Submit');
     $info['ispublic']=isset($info['ispublic'])?$info['ispublic']:1;
     $info['ticket_auto_response']=isset($info['ticket_auto_response'])?$info['ticket_auto_response']:1;
     $info['message_auto_response']=isset($info['message_auto_response'])?$info['message_auto_response']:1;
@@ -35,7 +35,7 @@ if($email && $_REQUEST['a']!='add'){
 }
 $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 ?>
-<h2>Email Address</h2>
+<h2><?php echo __('Email Address');?></h2>
 <form action="emails.php?<?php echo $qstr; ?>" method="post" id="save">
  <?php csrf_token(); ?>
  <input type="hidden" name="do" value="<?php echo $action; ?>">
@@ -46,14 +46,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em><strong>Email Information &amp; Settings</strong></em>
+                <em><strong><?php echo __('Email Information and Settings');?></strong></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <td width="180" class="required">
-                Email Address
+                <?php echo __('Email Address');?>
             </td>
             <td>
                 <input type="text" size="35" name="email" value="<?php echo $info['email']; ?>">
@@ -62,7 +62,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180" class="required">
-                Email Name
+                <?php echo __('Email Name');?>
             </td>
             <td>
                 <input type="text" size="35" name="name" value="<?php echo $info['name']; ?>">
@@ -71,17 +71,18 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>New Ticket Settings</strong></em>
+                <em><strong><?php echo __('New Ticket Settings'); ?></strong></em>
             </th>
         </tr>
         <tr>
             <td width="180">
-                Department
+                <?php echo __('Department');?>
             </td>
             <td>
         <span>
 			<select name="dept_id">
-			    <option value="0" selected="selected">&mdash; System Default &mdash;</option>
+			    <option value="0" selected="selected">&mdash; <?php
+                echo __('System Default'); ?> &mdash;</option>
 			    <?php
 			    $sql='SELECT dept_id, dept_name FROM '.DEPT_TABLE.' dept ORDER by dept_name';
 			    if(($res=db_query($sql)) && db_num_rows($res)){
@@ -99,12 +100,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-                Priority
+                <?php echo __('Priority'); ?>
             </td>
             <td>
 		<span>
 			<select name="priority_id">
-			    <option value="0" selected="selected">&mdash; System Default &mdash;</option>
+			    <option value="0" selected="selected">&mdash; <?php
+                echo __('System Default'); ?> &mdash;</option>
 			    <?php
 			    $sql='SELECT priority_id, priority_desc FROM '.PRIORITY_TABLE.' pri ORDER by priority_urgency DESC';
 			    if(($res=db_query($sql)) && db_num_rows($res)){
@@ -122,12 +124,12 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-                Help Topic
+                <?php echo __('Help Topic'); ?>
             </td>
             <td>
 		<span>
 			<select name="topic_id">
-			    <option value="0" selected="selected">&mdash; System Default &mdash;</option>
+                <option value="0" selected="selected">&mdash; <?php echo __('System Default'); ?> &mdash;</option>
 			    <?php
                     $topics = Topic::getHelpTopics();
                     while (list($id,$topic) = each($topics)) { ?>
@@ -144,23 +146,24 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-                Auto-Response
+                <?php echo __('Auto-Response'); ?>
             </td>
             <td>
                 <label><input type="checkbox" name="noautoresp" value="1" <?php echo $info['noautoresp']?'checked="checked"':''; ?> >
-                <strong>Disable</strong> for this Email Address
+                <?php echo __('<strong>Disable</strong> for this Email Address'); ?>
                 </label>
                 <i class="help-tip icon-question-sign" href="#auto_response"></i>
             </td>
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Email Login Information</strong>&nbsp;<i class="help-tip icon-question-sign" href="#login_information"></i></em>
+                <em><strong><?php echo __('Email Login Information'); ?></strong>
+                &nbsp;<i class="help-tip icon-question-sign" href="#login_information"></i></em>
             </th>
         </tr>
         <tr>
             <td width="180">
-                Username
+                <?php echo __('Username'); ?>
             </td>
             <td>
                 <input type="text" size="35" name="userid" value="<?php echo $info['userid']; ?>"
@@ -170,7 +173,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-               Password
+               <?php echo __('Password'); ?>
             </td>
             <td>
                 <input type="password" size="35" name="passwd" value="<?php echo $info['passwd']; ?>"
@@ -181,19 +184,21 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Fetching Email via IMAP or POP</strong>&nbsp;<i class="help-tip icon-question-sign" href="#mail_account"></i>&nbsp;<font class="error">&nbsp;<?php echo $errors['mail']; ?></font></em>
+                <em><strong><?php echo __('Fetching Email via IMAP or POP'); ?></strong>
+                &nbsp;<i class="help-tip icon-question-sign" href="#mail_account"></i>
+                &nbsp;<font class="error">&nbsp;<?php echo $errors['mail']; ?></font></em>
             </th>
         </tr>
         <tr>
-            <td>Status</td>
+            <td><?php echo __('Status'); ?></td>
             <td>
-                <label><input type="radio" name="mail_active"  value="1"   <?php echo $info['mail_active']?'checked="checked"':''; ?> />&nbsp;Enable</label>
+                <label><input type="radio" name="mail_active"  value="1"   <?php echo $info['mail_active']?'checked="checked"':''; ?> />&nbsp;<?php echo __('Enable'); ?></label>
                 &nbsp;&nbsp;
-                <label><input type="radio" name="mail_active"  value="0"   <?php echo !$info['mail_active']?'checked="checked"':''; ?> />&nbsp;Disable</label>
+                <label><input type="radio" name="mail_active"  value="0"   <?php echo !$info['mail_active']?'checked="checked"':''; ?> />&nbsp;<?php echo __('Disable'); ?></label>
                 &nbsp;<font class="error">&nbsp;<?php echo $errors['mail_active']; ?></font>
             </td>
         </tr>
-        <tr><td>Hostname</td>
+        <tr><td><?php echo __('Hostname'); ?></td>
             <td>
 		<span>
 			<input type="text" name="mail_host" size=35 value="<?php echo $info['mail_host']; ?>">
@@ -202,7 +207,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 		</span>
             </td>
         </tr>
-        <tr><td>Port Number</td>
+        <tr><td><?php echo __('Port Number'); ?></td>
             <td><input type="text" name="mail_port" size=6 value="<?php echo $info['mail_port']?$info['mail_port']:''; ?>">
 		<span>
 			&nbsp;<font class="error">&nbsp;<?php echo $errors['mail_port']; ?></font>
@@ -210,11 +215,11 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 		</span>
             </td>
         </tr>
-        <tr><td>Mail Box Protocol</td>
+        <tr><td><?php echo __('Mail Box Protocol'); ?></td>
             <td>
 		<span>
 			<select name="mail_proto">
-			    <option value=''>&mdash; Select Protocol &mdash;</option>
+                <option value=''>&mdash; <?php __('Select protocol'); ?> &mdash;</option>
 <?php
     foreach (MailFetcher::getSupportedProtos() as $proto=>$desc) { ?>
                 <option value="<?php echo $proto; ?>" <?php
@@ -228,87 +233,93 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             </td>
         </tr>
 
-        <tr><td>Fetch Frequency</td>
+        <tr><td><?php echo __('Fetch Frequency'); ?></td>
             <td>
 		<span>
-			<input type="text" name="mail_fetchfreq" size=4 value="<?php echo $info['mail_fetchfreq']?$info['mail_fetchfreq']:''; ?>"> minutes
+            <input type="text" name="mail_fetchfreq" size=4 value="<?php echo $info['mail_fetchfreq']?$info['mail_fetchfreq']:''; ?>"> <?php echo __('minutes'); ?>
 			<i class="help-tip icon-question-sign" href="#fetch_frequency"></i>
 			&nbsp;<font class="error">&nbsp;<?php echo $errors['mail_fetchfreq']; ?></font>
 		</span>
             </td>
         </tr>
-        <tr><td>Emails Per Fetch</td>
+        <tr><td><?php echo __('Emails Per Fetch'); ?></td>
             <td>
 		<span>
-			<input type="text" name="mail_fetchmax" size=4 value="<?php echo $info['mail_fetchmax']?$info['mail_fetchmax']:''; ?>"> emails
+			<input type="text" name="mail_fetchmax" size=4 value="<?php echo
+            $info['mail_fetchmax']?$info['mail_fetchmax']:''; ?>">
+            <?php echo __('emails'); ?>
 			<i class="help-tip icon-question-sign" href="#emails_per_fetch"></i>
 			&nbsp;<font class="error">&nbsp;<?php echo $errors['mail_fetchmax']; ?></font>
 		</span>
             </td>
         </tr>
-        <tr><td valign="top">Fetched Emails</td>
+        <tr><td valign="top"><?php echo __('Fetched Emails');?></td>
              <td>
                 <label><input type="radio" name="postfetch" value="archive" <?php echo ($info['postfetch']=='archive')? 'checked="checked"': ''; ?> >
-                 Move to folder: <input type="text" name="mail_archivefolder" size="20" value="<?php echo $info['mail_archivefolder']; ?>"/></label>
+                <?php echo __('Move to folder'); ?>:
+                <input type="text" name="mail_archivefolder" size="20" value="<?php echo $info['mail_archivefolder']; ?>"/></label>
                     &nbsp;<font class="error"><?php echo $errors['mail_folder']; ?></font>
                     <i class="help-tip icon-question-sign" href="#fetched_emails"></i>
                 <br/>
                 <label><input type="radio" name="postfetch" value="delete" <?php echo ($info['postfetch']=='delete')? 'checked="checked"': ''; ?> >
-                Delete emails</label>
+                <?php echo __('Delete emails'); ?></label>
                 <br/>
                 <label><input type="radio" name="postfetch" value="" <?php echo (isset($info['postfetch']) && !$info['postfetch'])? 'checked="checked"': ''; ?> >
-                 Do nothing <em>(not recommended)</em></label>
+                <?php echo __('Do nothing <em>(not recommended)</em>'); ?></label>
               <br /><font class="error"><?php echo $errors['postfetch']; ?></font>
             </td>
         </tr>
 
         <tr>
             <th colspan="2">
-                <em><strong>Sending Email via SMTP</strong>&nbsp;<i class="help-tip icon-question-sign" href="#smtp_settings"></i>&nbsp;<font class="error">&nbsp;<?php echo $errors['smtp']; ?></font></em>
+                <em><strong><?php echo __('Sending Email via SMTP'); ?></strong>
+                &nbsp;<i class="help-tip icon-question-sign" href="#smtp_settings"></i>
+                &nbsp;<font class="error">&nbsp;<?php echo $errors['smtp']; ?></font></em>
             </th>
         </tr>
-        <tr><td>Status</td>
+        <tr><td><?php echo __('Status');?></td>
             <td>
-                <label><input type="radio" name="smtp_active"  value="1"   <?php echo $info['smtp_active']?'checked':''; ?> />&nbsp;Enable</label>
+                <label><input type="radio" name="smtp_active" value="1" <?php echo $info['smtp_active']?'checked':''; ?> />&nbsp;<?php echo __('Enable');?></label>
                 &nbsp;
-                <label><input type="radio" name="smtp_active"  value="0"   <?php echo !$info['smtp_active']?'checked':''; ?> />&nbsp;Disable</label>
+                <label><input type="radio" name="smtp_active" value="0" <?php echo !$info['smtp_active']?'checked':''; ?> />&nbsp;<?php echo __('Disable');?></label>
                 &nbsp;<font class="error">&nbsp;<?php echo $errors['smtp_active']; ?></font>
             </td>
         </tr>
-        <tr><td>Hostname</td>
+        <tr><td><?php echo __('Hostname'); ?></td>
             <td><input type="text" name="smtp_host" size=35 value="<?php echo $info['smtp_host']; ?>">
                 &nbsp;<font class="error"><?php echo $errors['smtp_host']; ?></font>
 			<i class="help-tip icon-question-sign" href="#host_and_port"></i>
             </td>
         </tr>
-        <tr><td>Port Number</td>
+        <tr><td><?php echo __('Port Number'); ?></td>
             <td><input type="text" name="smtp_port" size=6 value="<?php echo $info['smtp_port']?$info['smtp_port']:''; ?>">
                 &nbsp;<font class="error"><?php echo $errors['smtp_port']; ?></font>
 			<i class="help-tip icon-question-sign" href="#host_and_port"></i>
             </td>
         </tr>
-        <tr><td>Authentication Required</td>
+        <tr><td><?php echo __('Authentication Required'); ?></td>
             <td>
 
                  <label><input type="radio" name="smtp_auth"  value="1"
-                    <?php echo $info['smtp_auth']?'checked':''; ?> /> Yes</label>
+                     <?php echo $info['smtp_auth']?'checked':''; ?> /> <?php echo __('Yes'); ?></label>
                  &nbsp;
                  <label><input type="radio" name="smtp_auth"  value="0"
-                    <?php echo !$info['smtp_auth']?'checked':''; ?> /> No</label>
+                     <?php echo !$info['smtp_auth']?'checked':''; ?> /> <?php echo __('No'); ?></label>
                 <font class="error">&nbsp;<?php echo $errors['smtp_auth']; ?></font>
             </td>
         </tr>
         <tr>
-            <td>Header Spoofing</td>
+            <td><?php echo __('Header Spoofing'); ?></td>
             <td>
                 <label><input type="checkbox" name="smtp_spoofing" value="1" <?php echo $info['smtp_spoofing'] ?'checked="checked"':''; ?>>
-                Allow for this Email Address</label>
+                <?php echo __('Allow for this Email Address'); ?></label>
                 <i class="help-tip icon-question-sign" href="#header_spoofing"></i>
             </td>
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Internal Notes</strong>: Admin's notes. &nbsp;<span class="error">&nbsp;<?php echo $errors['notes']; ?></span></em>
+                <em><strong><?php echo __('Internal Notes');?></strong>: <?php
+                echo __("be liberal, they're internal.");?> &nbsp;<span class="error">&nbsp;<?php echo $errors['notes']; ?></span></em>
             </th>
         </tr>
         <tr>
@@ -319,9 +330,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
     </tbody>
 </table>
-<p style="padding-left:225px;">
+<p style="text-align:center;">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="emails.php"'>
+    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="emails.php"'>
 </p>
 </form>
diff --git a/include/staff/emails.inc.php b/include/staff/emails.inc.php
index af919a762f4d9db08a1caa967d5b0b75df0e0323..62900ab33f615399d02c50a1a92df8115f05fe7c 100644
--- a/include/staff/emails.inc.php
+++ b/include/staff/emails.inc.php
@@ -37,20 +37,20 @@ $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
 $query="$sql GROUP BY email.email_id ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
 $res=db_query($query);
 if($res && ($num=db_num_rows($res)))
-    $showing=$pageNav->showing().' emails';
+    $showing=$pageNav->showing().' '.__('emails');
 else
-    $showing='No emails found!';
+    $showing=__('No emails found!');
 
 $def_dept_id = $cfg->getDefaultDeptId();
 $def_dept_name = $cfg->getDefaultDept()->getName();
 $def_priority = $cfg->getDefaultPriority()->getDesc();
 
 ?>
-<div style="width:700px;padding-top:5px; float:left;">
- <h2>Email Addresses</h2>
+<div class="pull-left" style="width:700px;padding-top:5px;">
+ <h2><?php echo __('Email Addresses');?></h2>
  </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
-    <b><a href="emails.php?a=add" class="Icon newEmail">Add New Email</a></b></div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
+    <b><a href="emails.php?a=add" class="Icon newEmail"><?php echo __('Add New Email');?></a></b></div>
 <div class="clear"></div>
 <form action="emails.php" method="POST" name="emails">
  <?php csrf_token(); ?>
@@ -60,12 +60,12 @@ $def_priority = $cfg->getDefaultPriority()->getDesc();
     <caption><?php echo $showing; ?></caption>
     <thead>
         <tr>
-            <th width="7">&nbsp;</th>        
-            <th width="400"><a <?php echo $email_sort; ?> href="emails.php?<?php echo $qstr; ?>&sort=email">Email</a></th>
-            <th width="120"><a  <?php echo $priority_sort; ?> href="emails.php?<?php echo $qstr; ?>&sort=priority">Priority</a></th>
-            <th width="250"><a  <?php echo $dept_sort; ?> href="emails.php?<?php echo $qstr; ?>&sort=dept">Department</a></th>
-            <th width="110" nowrap><a  <?php echo $created_sort; ?>href="emails.php?<?php echo $qstr; ?>&sort=created">Created</a></th>
-            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="emails.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th>
+            <th width="7">&nbsp;</th>
+            <th width="400"><a <?php echo $email_sort; ?> href="emails.php?<?php echo $qstr; ?>&sort=email"><?php echo __('Email');?></a></th>
+            <th width="120"><a  <?php echo $priority_sort; ?> href="emails.php?<?php echo $qstr; ?>&sort=priority"><?php echo __('Priority');?></a></th>
+            <th width="250"><a  <?php echo $dept_sort; ?> href="emails.php?<?php echo $qstr; ?>&sort=dept"><?php echo __('Department');?></a></th>
+            <th width="110" nowrap><a  <?php echo $created_sort; ?>href="emails.php?<?php echo $qstr; ?>&sort=created"><?php echo __('Created');?></a></th>
+            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="emails.php?<?php echo $qstr; ?>&sort=updated"><?php echo __('Last Updated');?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -88,7 +88,7 @@ $def_priority = $cfg->getDefaultPriority()->getDesc();
                   <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['email_id']; ?>"
                             <?php echo $sel?'checked="checked"':''; ?>  <?php echo $default?'disabled="disabled"':''; ?>>
                 </td>
-                <td><a href="emails.php?id=<?php echo $row['email_id']; ?>"><?php echo Format::htmlchars($email); ?></a>&nbsp;</td>
+                <td><span class="ltr"><a href="emails.php?id=<?php echo $row['email_id']; ?>"><?php echo Format::htmlchars($email); ?></a></span></td>
                 <td><?php echo $row['priority'] ?: $def_priority; ?></td>
                 <td><a href="departments.php?id=<?php $row['dept_id'] ?: $def_dept_id; ?>"><?php
                     echo $row['department'] ?: $def_dept_name; ?></a></td>
@@ -102,12 +102,12 @@ $def_priority = $cfg->getDefaultPriority()->getDesc();
      <tr>
         <td colspan="6">
             <?php if($res && $num){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No help emails found';
+                echo __('No help emails found');
             } ?>
         </td>
      </tr>
@@ -115,10 +115,10 @@ $def_priority = $cfg->getDefaultPriority()->getDesc();
 </table>
 <?php
 if($res && $num): //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="delete" value="Delete Email(s)" >
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete Email(s)');?>" >
 </p>
 <?php
 endif;
@@ -126,21 +126,22 @@ endif;
 </form>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected emails?</strong></font>
-        <br><br>Deleted emails CANNOT be recovered.
+        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
+            _N('selected email', 'selected emails', 2)) ;?></strong></font>
+        <br><br><?php echo __('Deleted data CANNOT be recovered.');?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/faq-categories.inc.php b/include/staff/faq-categories.inc.php
index 2ba6fa343bab5c85593ea845e852c0da271ecf81..9d9efa32ba994d56730d3b49f8fa62e343455fdb 100644
--- a/include/staff/faq-categories.inc.php
+++ b/include/staff/faq-categories.inc.php
@@ -2,13 +2,13 @@
 if(!defined('OSTSTAFFINC') || !$thisstaff) die('Access Denied');
 
 ?>
-<h2>Frequently Asked Questions&nbsp;<i class="help-tip icon-question-sign" href="#page_header"></i></h2>
+<h2><?php echo __('Frequently Asked Questions');?></h2>
 <form id="kbSearch" action="kb.php" method="get">
     <input type="hidden" name="a" value="search">
     <div>
         <input id="query" type="text" size="20" name="q" value="<?php echo Format::htmlchars($_REQUEST['q']); ?>">
         <select name="cid" id="cid">
-            <option value="">&mdash; All Categories &mdash;</option>
+            <option value="">&mdash; <?php echo __('All Categories');?> &mdash;</option>
             <?php
             $sql='SELECT category_id, name, count(faq.category_id) as faqs '
                 .' FROM '.FAQ_CATEGORY_TABLE.' cat '
@@ -26,11 +26,11 @@ if(!defined('OSTSTAFFINC') || !$thisstaff) die('Access Denied');
             }
             ?>
         </select>
-        <input id="searchSubmit" type="submit" value="Search">
+        <input id="searchSubmit" type="submit" value="<?php echo __('Search');?>">
     </div>
     <div>
         <select name="topicId" style="width:350px;" id="topic-id">
-            <option value="">&mdash; All Help Topics &mdash;</option>
+            <option value="">&mdash; <?php echo __('All Help Topics');?> &mdash;</option>
             <?php
             $sql='SELECT ht.topic_id, CONCAT_WS(" / ", pht.topic, ht.topic) as helptopic, count(faq.topic_id) as faqs '
                 .' FROM '.TOPIC_TABLE.' ht '
@@ -79,19 +79,19 @@ if($_REQUEST['q'] || $_REQUEST['cid'] || $_REQUEST['topicId']) { //Search.
 
     $sql.=' GROUP BY faq.faq_id ORDER BY question';
 
-    echo "<div><strong>Search Results</strong></div><div class='clear'></div>";
+    echo "<div><strong>".__('Search Results')."</strong></div><div class='clear'></div>";
     if(($res=db_query($sql)) && db_num_rows($res)) {
         echo '<div id="faq">
                 <ol>';
         while($row=db_fetch_array($res)) {
             echo sprintf('
                 <li><a href="faq.php?id=%d" class="previewfaq">%s</a> - <span>%s</span></li>',
-                $row['faq_id'],$row['question'],$row['ispublished']?'Published':'Internal');
+                $row['faq_id'],$row['question'],$row['ispublished']?__('Published'):__('Internal'));
         }
         echo '  </ol>
              </div>';
     } else {
-        echo '<strong class="faded">The search did not match any FAQs.</strong>';
+        echo '<strong class="faded">'.__('The search did not match any FAQs.').'</strong>';
     }
 } else { //Category Listing.
     $sql='SELECT cat.category_id, cat.name, cat.description, cat.ispublic, count(faq.faq_id) as faqs '
@@ -100,7 +100,7 @@ if($_REQUEST['q'] || $_REQUEST['cid'] || $_REQUEST['topicId']) { //Search.
         .' GROUP BY cat.category_id '
         .' ORDER BY cat.name';
     if(($res=db_query($sql)) && db_num_rows($res)) {
-        echo '<div>Click on a category to add new FAQs or manage its existing FAQs.&nbsp;</div>
+        echo '<div>'.__('Click on the category to browse FAQs or manage its existing FAQs.').'</div>
                 <ul id="kb">';
         while($row=db_fetch_array($res)) {
 
@@ -109,12 +109,12 @@ if($_REQUEST['q'] || $_REQUEST['cid'] || $_REQUEST['topicId']) { //Search.
                     <h4><a href="kb.php?cid=%d">%s (%d)</a> - <span>%s</span></h4>
                     %s
                 </li>',$row['category_id'],$row['name'],$row['faqs'],
-                ($row['ispublic']?'Public':'Internal'),
+                ($row['ispublic']?__('Public'):__('Internal')),
                 Format::safe_html($row['description']));
         }
         echo '</ul>';
     } else {
-        echo 'NO FAQs found';
+        echo __('NO FAQs found');
     }
 }
 ?>
diff --git a/include/staff/faq-category.inc.php b/include/staff/faq-category.inc.php
index f6f2ce4c59723e9b2e87e14b6cbdd2c7e9741214..8622a44abb8d6b9e7559f049d74a246fd2ba6c11 100644
--- a/include/staff/faq-category.inc.php
+++ b/include/staff/faq-category.inc.php
@@ -2,25 +2,25 @@
 if(!defined('OSTSTAFFINC') || !$category || !$thisstaff) die('Access Denied');
 
 ?>
-<div style="width:700px;padding-top:10px; float:left;">
-  <h2>Frequently Asked Questions</h2>
+<div class="pull-left" style="width:700px;padding-top:10px;">
+  <h2><?php echo __('Frequently Asked Questions');?></h2>
 </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">&nbsp;</div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">&nbsp;</div>
 <div class="clear"></div>
 <br>
 <div>
     <strong><?php echo $category->getName() ?></strong>
-    <span>(<?php echo $category->isPublic()?'Public':'Internal'; ?>)</span>
-    <time>Last updated <?php echo Format::db_date($category->getUpdateDate()); ?></time>
+    <span>(<?php echo $category->isPublic()?__('Public'):__('Internal'); ?>)</span>
+    <time> <?php echo __('Last updated').' '. Format::db_daydatetime($category->getUpdateDate()); ?></time>
 </div>
 <div class="cat-desc">
 <?php echo Format::display($category->getDescription()); ?>
 </div>
 <?php
 if($thisstaff->canManageFAQ()) {
-    echo sprintf('<div class="cat-manage-bar"><a href="categories.php?id=%d" class="Icon editCategory">Edit Category</a>
-             <a href="categories.php" class="Icon deleteCategory">Delete Category</a>
-             <a href="faq.php?cid=%d&a=add" class="Icon newFAQ">Add New FAQ</a></div>',
+    echo sprintf('<div class="cat-manage-bar"><a href="categories.php?id=%d" class="Icon editCategory">'.__('Edit Category').'</a>
+             <a href="categories.php" class="Icon deleteCategory">'.__('Delete Category').'</a>
+             <a href="faq.php?cid=%d&a=add" class="Icon newFAQ">'.__('Add New FAQ').'</a></div>',
             $category->getId(),
             $category->getId());
 } else {
@@ -41,11 +41,11 @@ if(($res=db_query($sql)) && db_num_rows($res)) {
     while($row=db_fetch_array($res)) {
         echo sprintf('
             <li><a href="faq.php?id=%d" class="previewfaq">%s <span>- %s</span></a></li>',
-            $row['faq_id'],$row['question'],$row['ispublished']?'Published':'Internal');
+            $row['faq_id'],$row['question'],$row['ispublished']?__('Published'):__('Internal'));
     }
     echo '  </ol>
          </div>';
 }else {
-    echo '<strong>Category does not have FAQs</strong>';
+    echo '<strong>'.__('Category does not have FAQs').'</strong>';
 }
 ?>
diff --git a/include/staff/faq-view.inc.php b/include/staff/faq-view.inc.php
index 5b6c528302636622ef4b8d87df3e1c934e658953..0c8824722e1a8c57bf1cf6b5dd6e64a86d7fe3f4 100644
--- a/include/staff/faq-view.inc.php
+++ b/include/staff/faq-view.inc.php
@@ -4,19 +4,19 @@ if(!defined('OSTSTAFFINC') || !$faq || !$thisstaff) die('Access Denied');
 $category=$faq->getCategory();
 
 ?>
-<h2>Frequently Asked Questions</h2>
+<h2><?php echo __('Frequently Asked Questions');?></h2>
 <div id="breadcrumbs">
-    <a href="kb.php">All Categories</a>
+    <a href="kb.php"><?php echo __('All Categories');?></a>
     &raquo; <a href="kb.php?cid=<?php echo $category->getId(); ?>"><?php echo $category->getName(); ?></a>
-    <span class="faded">(<?php echo $category->isPublic()?'Public':'Internal'; ?>)</span>
+    <span class="faded">(<?php echo $category->isPublic()?__('Public'):__('Internal'); ?>)</span>
 </div>
-<div style="width:700px;padding-top:2px; float:left;">
-<strong style="font-size:16px;"><?php echo $faq->getQuestion() ?></strong>&nbsp;&nbsp;<span class="faded"><?php echo $faq->isPublished()?'(Published)':''; ?></span>
+<div class="pull-left" style="width:700px;padding-top:2px;">
+<strong style="font-size:16px;"><?php echo $faq->getQuestion() ?></strong>&nbsp;&nbsp;<span class="faded"><?php echo '('.$faq->isPublished()?__('Published').')':''; ?></span>
 </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
 <?php
 if($thisstaff->canManageFAQ()) {
-    echo sprintf('<a href="faq.php?id=%d&a=edit" class="Icon newHelpTopic">Edit FAQ</a>',
+    echo sprintf('<a href="faq.php?id=%d&a=edit" class="Icon newHelpTopic">'.__('Edit FAQ').'</a>',
             $faq->getId());
 }
 ?>
@@ -28,12 +28,12 @@ if($thisstaff->canManageFAQ()) {
 </div>
 <div class="clear"></div>
 <p>
- <div><span class="faded"><b>Attachments:</b></span> <?php echo $faq->getAttachmentsLinks(); ?></div>
- <div><span class="faded"><b>Help Topics:</b></span>
+ <div><span class="faded"><b><?php echo __('Attachments');?>:</b></span> <?php echo $faq->getAttachmentsLinks(); ?></div>
+ <div><span class="faded"><b><?php echo __('Help Topics');?>:</b></span>
     <?php echo ($topics=$faq->getHelpTopics())?implode(', ',$topics):' '; ?>
     </div>
 </p>
-<div class="faded">&nbsp;Last updated <?php echo Format::db_daydatetime($faq->getUpdateDate()); ?></div>
+<div class="faded">&nbsp;<?php echo __('Last updated');?> <?php echo Format::db_daydatetime($category->getUpdateDate()); ?></div>
 <hr>
 <?php
 if($thisstaff->canManageFAQ()) {
@@ -45,21 +45,21 @@ if($thisstaff->canManageFAQ()) {
         <input type="hidden" name="id" value="<?php echo  $faq->getId(); ?>">
         <input type="hidden" name="do" value="manage-faq">
         <div>
-            <strong>Options: </strong>
+            <strong><?php echo __('Options');?>: </strong>
             <select name="a" style="width:200px;">
-                <option value="">Select Action</option>
+                <option value=""><?php echo __('Select Action');?></option>
                 <?php
                 if($faq->isPublished()) { ?>
-                <option value="unpublish">Unpublish FAQ</option>
+                <option value="unpublish"><?php echo __('Unpublish FAQ');?></option>
                 <?php
                 }else{ ?>
-                <option value="publish">Publish FAQ</option>
+                <option value="publish"><?php echo __('Publish FAQ');?></option>
                 <?php
                 } ?>
-                <option value="edit">Edit FAQ</option>
-                <option value="delete">Delete FAQ</option>
+                <option value="edit"><?php echo __('Edit FAQ');?></option>
+                <option value="delete"><?php echo __('Delete FAQ');?></option>
             </select>
-            &nbsp;&nbsp;<input type="submit" name="submit" value="Go">
+            &nbsp;&nbsp;<input type="submit" name="submit" value="<?php echo __('Go');?>">
         </div>
     </form>
    </div>
diff --git a/include/staff/faq.inc.php b/include/staff/faq.inc.php
index 8dbaa2ecdc282026bca6581a7a8f55c47a40943b..4fcea8138b348ae8bdd24ddd3316cc4e940826ab 100644
--- a/include/staff/faq.inc.php
+++ b/include/staff/faq.inc.php
@@ -3,9 +3,9 @@ if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->canManageFAQ()) die('Acc
 $info=array();
 $qstr='';
 if($faq){
-    $title='Update FAQ: '.$faq->getQuestion();
+    $title=__('Update FAQ').': '.$faq->getQuestion();
     $action='update';
-    $submit_text='Save Changes';
+    $submit_text=__('Save Changes');
     $info=$faq->getHashtable();
     $info['id']=$faq->getId();
     $info['topics']=$faq->getHelpTopicsIds();
@@ -13,9 +13,9 @@ if($faq){
     $info['notes']=Format::viewableImages($faq->getNotes());
     $qstr='id='.$faq->getId();
 }else {
-    $title='Add New FAQ';
+    $title=__('Add New FAQ');
     $action='create';
-    $submit_text='Add FAQ';
+    $submit_text=__('Add FAQ');
     if($category) {
         $qstr='cid='.$category->getId();
         $info['category_id']=$category->getId();
@@ -29,7 +29,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="do" value="<?php echo $action; ?>">
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2>FAQ</h2>
+ <h2><?php echo __('FAQ');?></h2>
  <table class="form_table fixed" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
         <tr><td></td><td></td></tr> <!-- For fixed table layout -->
@@ -42,20 +42,20 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
     <tbody>
         <tr>
             <th colspan="2">
-                <em>FAQ Information</em>
+                <em><?php echo __('FAQ Information');?></em>
             </th>
         </tr>
         <tr>
             <td colspan=2>
-                <div style="padding-top:3px;"><b>Question</b>&nbsp;<span class="error">*&nbsp;<?php echo $errors['question']; ?></span></div>
+                <div style="padding-top:3px;"><b><?php echo __('Question');?></b>&nbsp;<span class="error">*&nbsp;<?php echo $errors['question']; ?></span></div>
                     <input type="text" size="70" name="question" value="<?php echo $info['question']; ?>">
             </td>
         </tr>
         <tr>
             <td colspan=2>
-                <div><b>Category Listing</b>:&nbsp;<span class="faded">FAQ category the question belongs to.</span></div>
+                <div><b><?php echo __('Category Listing');?></b>:&nbsp;<span class="faded"><?php echo __('FAQ category the question belongs to.');?></span></div>
                 <select name="category_id" style="width:350px;">
-                    <option value="0">Select FAQ Category </option>
+                    <option value="0"><?php echo __('Select FAQ Category');?> </option>
                     <?php
                     $sql='SELECT category_id, name, ispublic FROM '.FAQ_CATEGORY_TABLE;
                     if(($res=db_query($sql)) && db_num_rows($res)) {
@@ -64,7 +64,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                                     $row['category_id'],
                                     (($info['category_id']==$row['category_id'])?'selected="selected"':''),
                                     $row['name'],
-                                    ($info['ispublic']?'Public':'Internal'));
+                                    ($info['ispublic']?__('Public'):__('Internal')));
                         }
                     }
                    ?>
@@ -74,17 +74,18 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td colspan=2>
-                <div><b>Listing Type</b>: &nbsp;<i class="help-tip icon-question-sign" href="#listing_type"></i></div>
-                <input type="radio" name="ispublished" value="1" <?php echo $info['ispublished']?'checked="checked"':''; ?>>Public (publish)
+                <div><b><?php echo __('Listing Type');?></b>:
+                &nbsp;<i class="help-tip icon-question-sign" href="#listing_type"></i></div>
+                <input type="radio" name="ispublished" value="1" <?php echo $info['ispublished']?'checked="checked"':''; ?>><?php echo __('Public (publish)');?>
                 &nbsp;&nbsp;&nbsp;&nbsp;
-                <input type="radio" name="ispublished" value="0" <?php echo !$info['ispublished']?'checked="checked"':''; ?>>Internal (private)
+                <input type="radio" name="ispublished" value="0" <?php echo !$info['ispublished']?'checked="checked"':''; ?>><?php echo __('Internal (private)');?>
                 &nbsp;<span class="error">*&nbsp;<?php echo $errors['ispublished']; ?></span>
             </td>
         </tr>
         <tr>
             <td colspan=2>
                 <div style="margin-bottom:0.5em;margin-top:0.5em">
-                    <b>Answer</b>&nbsp;<font class="error">*&nbsp;<?php echo $errors['answer']; ?></font>
+                    <b><?php echo __('Answer');?></b>&nbsp;<font class="error">*&nbsp;<?php echo $errors['answer']; ?></font></div>
                 </div>
                 <textarea name="answer" cols="21" rows="12"
                     style="width:98%;" class="richtext draft"
@@ -95,31 +96,27 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td colspan=2>
-                <div><b>Attachments</b> (optional) <font class="error">&nbsp;<?php echo $errors['files']; ?></font></div>
+                <div><h3><?php echo __('Attachments');?>
+                    <span class="faded">(<?php echo __('optional');?>)</span></h3>
+                    <div class="error"><?php echo $errors['files']; ?></div>
+                </div>
                 <?php
-                if($faq && ($files=$faq->attachments->getSeparates())) {
-                    echo '<div class="faq_attachments"><span class="faded">Uncheck to delete the attachment on submit</span><br>';
-                    foreach($files as $file) {
-                        $hash=$file['key'].md5($file['id'].session_id().strtolower($file['key']));
-                        echo sprintf('<label><input type="checkbox" name="files[]" id="f%d" value="%d" checked="checked">
-                                      <a href="file.php?h=%s">%s</a>&nbsp;&nbsp;</label>&nbsp;',
-                                      $file['id'], $file['id'], $hash, $file['name']);
-                    }
-                    echo '</div><br>';
+                $attachments = $faq_form->getField('attachments');
+                if ($faq && ($files=$faq->attachments->getSeparates())) {
+                    $ids = array();
+                    foreach ($files as $f)
+                        $ids[] = $f['id'];
+                    $attachments->value = $ids;
                 }
-                ?>
-                <div class="faded">Select files to upload.</div>
-                <div class="uploads"></div>
-                <div class="file_input">
-                    <input type="file" class="multifile" name="attachments[]" size="30" value="" />
-                </div>
+                print $attachments->render(); ?>
+                <br/>
             </td>
         </tr>
         <?php
         if ($topics = Topic::getAllHelpTopics()) { ?>
         <tr>
             <th colspan="2">
-                <em><strong>Help Topics</strong>: Check all help topics related to this FAQ.</em>
+                <em><strong><?php echo __('Help Topics');?></strong>: <?php echo __('Check all help topics related to this FAQ.');?></em>
             </th>
         </tr>
         <tr><td colspan="2">
@@ -137,7 +134,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         } ?>
         <tr>
             <th colspan="2">
-                <em><strong>Internal Notes</strong>: &nbsp;</em>
+                <em><strong><?php echo __('Internal Notes');?></strong>: &nbsp;</em>
             </th>
         </tr>
         <tr>
@@ -148,12 +145,12 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
     </tbody>
 </table>
-<p style="padding-left:225px;">
+<p style="text-align:center;">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset" onclick="javascript:
+    <input type="reset"  name="reset"  value="<?php echo __('Reset'); ?>" onclick="javascript:
         $(this.form).find('textarea.richtext')
             .redactor('deleteDraft');
         location.reload();" />
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="faq.php?<?php echo $qstr; ?>"'>
+    <input type="button" name="cancel" value="<?php echo __('Cancel'); ?>" onclick='window.location.href="faq.php?<?php echo $qstr; ?>"'>
 </p>
 </form>
diff --git a/include/staff/filter.inc.php b/include/staff/filter.inc.php
index f2339c537e5ca048f894589a67d869899050f806..3bda64e043f56e6106e758ebdcc36988a217e4ce 100644
--- a/include/staff/filter.inc.php
+++ b/include/staff/filter.inc.php
@@ -7,16 +7,16 @@ $match_types=Filter::getSupportedMatchTypes();
 $info=array();
 $qstr='';
 if($filter && $_REQUEST['a']!='add'){
-    $title='Update Filter';
+    $title=__('Update Filter');
     $action='update';
-    $submit_text='Save Changes';
+    $submit_text=__('Save Changes');
     $info=array_merge($filter->getInfo(),$filter->getFlatRules());
     $info['id']=$filter->getId();
     $qstr.='&id='.$filter->getId();
 }else {
-    $title='Add New Filter';
+    $title=__('Add New Filter');
     $action='add';
-    $submit_text='Add Filter';
+    $submit_text=__('Add Filter');
     $info['isactive']=isset($info['isactive'])?$info['isactive']:0;
     $qstr.='&a='.urlencode($_REQUEST['a']);
 }
@@ -27,20 +27,20 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="do" value="<?php echo $action; ?>">
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2>Ticket Filter</h2>
+ <h2><?php echo __('Ticket Filter');?></h2>
  <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em>Filters are executed based on execution order. Filter can target specific ticket source.</em>
+                <em><?php echo __('Filters are executed based on execution order. Filter can target specific ticket source.');?></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <td width="180" class="required">
-              Filter Name:
+              <?php echo __('Filter Name');?>:
             </td>
             <td>
                 <input type="text" size="30" name="name" value="<?php echo $info['name']; ?>">
@@ -49,35 +49,37 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180" class="required">
-              Execution Order:
+              <?php echo __('Execution Order');?>:
             </td>
             <td>
                 <input type="text" size="6" name="execorder" value="<?php echo $info['execorder']; ?>">
-                <em>(1...99 )</em>
+                <em>(1...99)</em>
                 &nbsp;<span class="error">*&nbsp;<?php echo $errors['execorder']; ?></span>
                 &nbsp;&nbsp;&nbsp;
                 <input type="checkbox" name="stop_onmatch" value="1" <?php echo $info['stop_onmatch']?'checked="checked"':''; ?> >
-                <strong>Stop</strong> processing further on match!&nbsp;<i class="help-tip icon-question-sign" href="#execution_order"></i>
+                <?php echo __('<strong>Stop</strong> processing further on match!');?>
+                &nbsp;<i class="help-tip icon-question-sign" href="#execution_order"></i>
             </td>
         </tr>
         <tr>
             <td width="180" class="required">
-                Filter Status:
+                <?php echo __('Filter Status');?>:
             </td>
             <td>
                 <input type="radio" name="isactive" value="1" <?php echo
-                $info['isactive']?'checked="checked"':''; ?>> Active
-                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>> Disabled
+                $info['isactive']?'checked="checked"':''; ?>> <?php echo __('Active'); ?>
+                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>
+                > <?php echo __('Disabled'); ?>
                 &nbsp;<span class="error">*&nbsp;</span>
             </td>
         </tr>
         <tr>
             <td width="180" class="required">
-                Target Channel:
+                <?php echo __('Target Channel');?>:
             </td>
             <td>
                 <select name="target">
-                   <option value="">&mdash; Select a Channel &dash;</option>
+                   <option value="">&mdash; <?php echo __('Select a Channel');?> &dash;</option>
                    <?php
                    foreach(Filter::getTargets() as $k => $v) {
                        echo sprintf('<option value="%s" %s>%s</option>',
@@ -85,7 +87,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                     }
                     $sql='SELECT email_id,email,name FROM '.EMAIL_TABLE.' email ORDER by name';
                     if(($res=db_query($sql)) && db_num_rows($res)) {
-                        echo '<OPTGROUP label="Specific System Email">';
+                        echo sprintf('<OPTGROUP label="%s">', __('System Emails'));
                         while(list($id,$email,$name)=db_fetch_row($res)) {
                             $selected=($info['email_id'] && $id==$info['email_id'])?'selected="selected"':'';
                             if($name)
@@ -103,19 +105,21 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Filter Rules</strong>: Rules are applied based on the criteria.&nbsp;<span class="error">*&nbsp;<?php echo
+                <em><strong><?php echo __('Filter Rules');?></strong>: <?php
+                echo __('Rules are applied based on the criteria.');?>&nbsp;<span class="error">*&nbsp;<?php echo
                 $errors['rules']; ?></span></em>
             </th>
         </tr>
         <tr>
             <td colspan=2>
-               <em>Rules Matching Criteria:</em>
+               <em><?php echo __('Rules Matching Criteria');?>:</em>
                 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
-                <input type="radio" name="match_all_rules" value="1" <?php echo $info['match_all_rules']?'checked="checked"':''; ?>>Match All
+                <input type="radio" name="match_all_rules" value="1" <?php echo $info['match_all_rules']?'checked="checked"':''; ?>><?php echo __('Match All');?>
                 &nbsp;&nbsp;&nbsp;
-                <input type="radio" name="match_all_rules" value="0" <?php echo !$info['match_all_rules']?'checked="checked"':''; ?>>Match Any
+                <input type="radio" name="match_all_rules" value="0" <?php echo !$info['match_all_rules']?'checked="checked"':''; ?>><?php echo __('Match Any');?>
                 &nbsp;<span class="error">*&nbsp;</span>
-                <em>(case-insensitive comparison)</em>&nbsp;<i class="help-tip icon-question-sign" href="#rules_matching_criteria"></i>
+                <em>(<?php echo __('case-insensitive comparison');?>)</em>
+                &nbsp;<i class="help-tip icon-question-sign" href="#rules_matching_criteria"></i>
 
             </td>
         </tr>
@@ -126,31 +130,33 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             <td colspan="2">
                 <div>
                     <select style="max-width: 200px;" name="rule_w<?php echo $i; ?>">
-                        <option value="">&mdash; Select One &dash;</option>
+                        <option value="">&mdash; <?php echo __('Select One');?> &mdash;</option>
                         <?php
                         foreach ($matches as $group=>$ms) { ?>
-                            <optgroup label="<?php echo $group; ?>"><?php
+                            <optgroup label="<?php echo __($group); ?>"><?php
                             foreach ($ms as $k=>$v) {
                                 $sel=($info["rule_w$i"]==$k)?'selected="selected"':'';
-                                echo sprintf('<option value="%s" %s>%s</option>',$k,$sel,$v);
+                                echo sprintf('<option value="%s" %s>%s</option>',
+                                    $k,$sel,__($v));
                             } ?>
                         </optgroup>
                         <?php } ?>
                     </select>
                     <select name="rule_h<?php echo $i; ?>">
-                        <option value="0">&mdash; Select One &dash;</option>
+                        <option value="0">&mdash; <?php echo __('Select One');?> &dash;</option>
                         <?php
                         foreach($match_types as $k=>$v){
                             $sel=($info["rule_h$i"]==$k)?'selected="selected"':'';
-                            echo sprintf('<option value="%s" %s>%s</option>',$k,$sel,$v);
+                            echo sprintf('<option value="%s" %s>%s</option>',
+                                $k,$sel,$v);
                         }
                         ?>
                     </select>&nbsp;
-                    <input type="text" size="60" name="rule_v<?php echo $i; ?>" value="<?php echo $info["rule_v$i"]; ?>">
+                    <input class="ltr" type="text" size="60" name="rule_v<?php echo $i; ?>" value="<?php echo $info["rule_v$i"]; ?>">
                     &nbsp;<span class="error">&nbsp;<?php echo $errors["rule_$i"]; ?></span>
                 <?php
                 if($info["rule_w$i"] || $info["rule_h$i"] || $info["rule_v$i"]){ ?>
-                <div style="float:right;text-align:right;padding-right:20px;"><a href="#" class="clearrule">(clear)</a></div>
+                <div class="pull-right" style="padding-right:20px;"><a href="#" class="clearrule">(<?php echo __('clear');?>)</a></div>
                 <?php
                 } ?>
                 </div>
@@ -162,43 +168,47 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         } ?>
         <tr>
             <th colspan="2">
-                <em><strong>Filter Actions</strong>: Can be overridden by other filters depending on processing order.&nbsp;</em>
+                <em><strong><?php echo __('Filter Actions');?></strong>: <?php
+                echo __('Can be overwridden by other filters depending on processing order.');?>&nbsp;</em>
             </th>
         </tr>
         <tr>
             <td width="180">
-                Reject Ticket:
+                <?php echo __('Reject Ticket');?>:
             </td>
             <td>
                 <input type="checkbox" name="reject_ticket" value="1" <?php echo $info['reject_ticket']?'checked="checked"':''; ?> >
-                    <strong><font class="error">Reject Ticket</font></strong>&nbsp;<i class="help-tip icon-question-sign" href="#reject_ticket"></i>
+                    <strong><font class="error"><?php echo __('Reject Ticket');?></font></strong>
+                    &nbsp;<i class="help-tip icon-question-sign" href="#reject_ticket"></i>
             </td>
         </tr>
         <tr>
             <td width="180">
-                Reply-To Email:
+                <?php echo __('Reply-To Email');?>:
             </td>
             <td>
                 <input type="checkbox" name="use_replyto_email" value="1" <?php echo $info['use_replyto_email']?'checked="checked"':''; ?> >
-                    <strong>Use</strong> Reply-To Email <em>(if available)&nbsp;<i class="help-tip icon-question-sign" href="#reply_to_email"></i></em>
+                    <?php echo __('<strong>Use</strong> Reply-To Email');?> <em>(<?php echo __('if available');?>)</em>
+                    &nbsp;<i class="help-tip icon-question-sign" href="#reply_to_email"></i></em>
             </td>
         </tr>
         <tr>
             <td width="180">
-                Ticket auto-response:
+                <?php echo __('Ticket auto-response');?>:
             </td>
             <td>
                 <input type="checkbox" name="disable_autoresponder" value="1" <?php echo $info['disable_autoresponder']?'checked="checked"':''; ?> >
-                    <strong>Disable</strong> auto-response.&nbsp;<i class="help-tip icon-question-sign" href="#ticket_auto_response"></i>
+                    <?php echo __('<strong>Disable</strong> auto-response.');?>
+                    &nbsp;<i class="help-tip icon-question-sign" href="#ticket_auto_response"></i>
             </td>
         </tr>
         <tr>
             <td width="180">
-                Canned Response:
+                <?php echo __('Canned Response');?>:
             </td>
                 <td>
                 <select name="canned_response_id">
-                    <option value="">&mdash; None &mdash;</option>
+                    <option value="">&mdash; <?php echo __('None');?> &mdash;</option>
                     <?php
                     $sql='SELECT canned_id, title, isenabled FROM '.CANNED_TABLE .' ORDER by title';
                     if ($res=db_query($sql)) {
@@ -208,7 +218,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                                 ? 'selected="selected"' : '';
 
                             if (!$isenabled)
-                                $title .= ' (disabled)';
+                                $title .= ' ' . __('(disabled)');
 
                             echo sprintf('<option value="%d" %s>%s</option>',
                                 $id, $selected, $title);
@@ -221,11 +231,11 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-                Department:
+                <?php echo __('Department');?>:
             </td>
             <td>
                 <select name="dept_id">
-                    <option value="">&mdash; Default &mdash;</option>
+                    <option value="">&mdash; <?php echo __('Default');?> &mdash;</option>
                     <?php
                     $sql='SELECT dept_id,dept_name FROM '.DEPT_TABLE.' dept ORDER by dept_name';
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -241,11 +251,41 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-                Priority:
+                <?php echo __('Status'); ?>:
+            </td>
+            <td>
+                <span>
+                <select name="status_id">
+                    <option value="">&mdash; <?php echo __('Default'); ?> &mdash;</option>
+                    <?php
+                    foreach (TicketStatusList::getStatuses() as $status) {
+                        $name = $status->getName();
+                        if (!($isenabled = $status->isEnabled()))
+                            $name.=' '.__('(disabled)');
+
+                        echo sprintf('<option value="%d" %s %s>%s</option>',
+                                $status->getId(),
+                                ($info['status_id'] == $status->getId())
+                                 ? 'selected="selected"' : '',
+                                 $isenabled ? '' : 'disabled="disabled"',
+                                 $name
+                                );
+                    }
+                    ?>
+                </select>
+                &nbsp;
+                <span class="error"><?php echo $errors['status_id']; ?></span>
+                <i class="help-tip icon-question-sign" href="#status"></i>
+                </span>
+            </td>
+        </tr>
+        <tr>
+            <td width="180">
+                <?php echo __('Priority');?>:
             </td>
             <td>
                 <select name="priority_id">
-                    <option value="">&mdash; Default &mdash;</option>
+                    <option value="">&mdash; <?php echo __('Default');?> &mdash;</option>
                     <?php
                     $sql='SELECT priority_id,priority_desc FROM '.PRIORITY_TABLE.' pri ORDER by priority_urgency DESC';
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -262,11 +302,11 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-                SLA Plan:
+                <?php echo __('SLA Plan');?>:
             </td>
             <td>
                 <select name="sla_id">
-                    <option value="0">&mdash; System Default &mdash;</option>
+                    <option value="0">&mdash; <?php echo __('System Default');?> &mdash;</option>
                     <?php
                     if($slas=SLA::getSLAs()) {
                         foreach($slas as $id =>$name) {
@@ -282,14 +322,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-                Auto-assign To:
+                <?php echo __('Auto-assign To');?>:
             </td>
             <td>
                 <select name="assign">
-                    <option value="0">&mdash; Unassigned &mdash;</option>
+                    <option value="0">&mdash; <?php echo __('Unassigned');?> &mdash;</option>
                     <?php
                     if (($users=Staff::getStaffMembers())) {
-                        echo '<OPTGROUP label="Staff Members">';
+                        echo '<OPTGROUP label="'.__('Agents').'">';
                         foreach($users as $id => $name) {
                             $name = new PersonsName($name);
                             $k="s$id";
@@ -302,7 +342,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                     }
                     $sql='SELECT team_id, isenabled, name FROM '.TEAM_TABLE .' ORDER BY name';
                     if(($res=db_query($sql)) && db_num_rows($res)){
-                        echo '<OPTGROUP label="Teams">';
+                        echo '<OPTGROUP label="'.__('Teams').'">';
                         while (list($id, $isenabled, $name) = db_fetch_row($res)){
                             $k="t$id";
                             $selected = ($info['assign']==$k || $info['team_id']==$id)?'selected="selected"':'';
@@ -322,11 +362,12 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-                Help Topic
+                <?php echo __('Help Topic'); ?>
             </td>
             <td>
                 <select name="topic_id">
-                    <option value="0" selected="selected">&mdash; Unchanged &mdash;</option>
+                    <option value="0" selected="selected">&mdash; <?php
+                        echo __('Unchanged'); ?> &mdash;</option>
                     <?php
                     $sql='SELECT topic_id, topic FROM '.TOPIC_TABLE.' T ORDER by topic';
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -342,7 +383,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Admin Notes</strong>: Internal notes.</em>
+                <em><strong><?php echo __('Internal Notes');?></strong>: <?php
+                    echo __("be liberal, they're internal");?></em>
             </th>
         </tr>
         <tr>
@@ -353,9 +395,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
     </tbody>
 </table>
-<p style="padding-left:225px;">
+<p style="text-align:center;">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="filters.php"'>
+    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="filters.php"'>
 </p>
 </form>
diff --git a/include/staff/filters.inc.php b/include/staff/filters.inc.php
index 5e90c32052bb001d00ecd4de6d4d3217ff1453f8..76a62a107b0a868d1dcadf1131cae562c8110561 100644
--- a/include/staff/filters.inc.php
+++ b/include/staff/filters.inc.php
@@ -38,17 +38,17 @@ $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
 $query="$sql ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
 $res=db_query($query);
 if($res && ($num=db_num_rows($res)))
-    $showing=$pageNav->showing().' filters';
+    $showing=$pageNav->showing().' '._N('filter', 'filters', $num);
 else
-    $showing='No filters found!';
+    $showing=__('No filters found!');
 
 ?>
 
-<div style="width:700px;padding-top:5px; float:left;">
- <h2>Ticket Filters</h2>
+<div class="pull-left" style="width:700px;padding-top:5px;">
+ <h2><?php echo __('Ticket Filters');?></h2>
 </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
- <b><a href="filters.php?a=add" class="Icon newEmailFilter">Add New Filter</a></b></div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
+ <b><a href="filters.php?a=add" class="Icon newEmailFilter"><?php echo __('Add New Filter');?></a></b></div>
 <div class="clear"></div>
 <form action="filters.php" method="POST" name="filters">
  <?php csrf_token(); ?>
@@ -58,14 +58,14 @@ else
     <caption><?php echo $showing; ?></caption>
     <thead>
         <tr>
-            <th width="7">&nbsp;</th>        
-            <th width="320"><a <?php echo $name_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=name">Name</a></th>
-            <th width="80"><a  <?php echo $status_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=status">Status</a></th>
-            <th width="80" style="text-align:center;"><a  <?php echo $order_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=order">Order</a></th>
-            <th width="80" style="text-align:center;"><a  <?php echo $rules_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=rules">Rules</a></th>
-            <th width="100"><a  <?php echo $target_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=target">Target</a></th>
-            <th width="120" nowrap><a  <?php echo $created_sort; ?>href="filters.php?<?php echo $qstr; ?>&sort=created">Date Added</a></th>
-            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="filters.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th>
+            <th width="7">&nbsp;</th>
+            <th width="320"><a <?php echo $name_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=name"><?php echo __('Name');?></a></th>
+            <th width="80"><a  <?php echo $status_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=status"><?php echo __('Status');?></a></th>
+            <th width="80" style="text-align:center;"><a  <?php echo $order_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=order"><?php echo __('Order');?></a></th>
+            <th width="80" style="text-align:center;"><a  <?php echo $rules_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=rules"><?php echo __('Rules');?></a></th>
+            <th width="100"><a  <?php echo $target_sort; ?> href="filters.php?<?php echo $qstr; ?>&sort=target"><?php echo __('Target');?></a></th>
+            <th width="120" nowrap><a  <?php echo $created_sort; ?>href="filters.php?<?php echo $qstr; ?>&sort=created"><?php echo __('Date Added');?></a></th>
+            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="filters.php?<?php echo $qstr; ?>&sort=updated"><?php echo __('Last Updated');?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -80,11 +80,11 @@ else
                 ?>
             <tr id="<?php echo $row['id']; ?>">
                 <td width=7px>
-                  <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['id']; ?>" 
+                  <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['id']; ?>"
                             <?php echo $sel?'checked="checked"':''; ?>>
                 </td>
                 <td>&nbsp;<a href="filters.php?id=<?php echo $row['id']; ?>"><?php echo Format::htmlchars($row['name']); ?></a></td>
-                <td><?php echo $row['isactive']?'Active':'<b>Disabled</b>'; ?></td>
+                <td><?php echo $row['isactive']?__('Active'):'<b>'.__('Disabled').'</b>'; ?></td>
                 <td style="text-align:right;padding-right:25px;"><?php echo $row['execorder']; ?>&nbsp;</td>
                 <td style="text-align:right;padding-right:25px;"><?php echo $row['rules']; ?>&nbsp;</td>
                 <td>&nbsp;<?php echo Format::htmlchars($targets[$row['target']]); ?></td>
@@ -98,12 +98,12 @@ else
      <tr>
         <td colspan="8">
             <?php if($res && $num){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No filters found';
+                echo __('No filters found');
             } ?>
         </td>
      </tr>
@@ -111,12 +111,12 @@ else
 </table>
 <?php
 if($res && $num): //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="enable" value="Enable">
-    <input class="button" type="submit" name="disable" value="Disable">
-    <input class="button" type="submit" name="delete" value="Delete">
+    <input class="button" type="submit" name="enable" value="<?php echo __('Enable');?>">
+    <input class="button" type="submit" name="disable" value="<?php echo __('Disable');?>">
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete');?>">
 </p>
 <?php
 endif;
@@ -124,27 +124,30 @@ endif;
 </form>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="enable-confirm">
-        Are you sure want to <b>enable</b> selected filters?
+        <?php echo sprintf(__('Are you sure want to <b>enable</b> %s?'),
+            _N('selected filter', 'selected filters', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="disable-confirm">
-        Are you sure want to <b>disable</b>  selected filters?
+        <?php echo sprintf(__('Are you sure want to <b>disable</b> %s?'),
+            _N('selected filter', 'selected filters', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected filters?</strong></font>
-        <br><br>Deleted filters CANNOT be recovered, including any associated rules.
+        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
+            _N('selected filter', 'selected filters', 2));?></strong></font>
+        <br><br><?php echo __('Deleted data CANNOT be recovered, including any associated rules.');?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/footer.inc.php b/include/staff/footer.inc.php
index 529fe8bfcd81aa4014ce0cbbb97a76b784d63f1a..12c9b034dfe457bd9ff7dbff974b754250858de8 100644
--- a/include/staff/footer.inc.php
+++ b/include/staff/footer.inc.php
@@ -1,38 +1,60 @@
-<?php if (!isset($_SERVER['HTTP_X_PJAX'])) { ?>
-    </div>
-    <div id="footer">
-        Copyright &copy; 2006-<?php echo date('Y'); ?>&nbsp;<?php echo (string) $ost->company ?: 'osTicket.com'; ?>&nbsp;All Rights Reserved.
-    </div>
-<?php
-if(is_object($thisstaff) && $thisstaff->isStaff()) { ?>
-    <div>
-        <!-- Do not remove <img src="autocron.php" alt="" width="1" height="1" border="0" /> or your auto cron will cease to function -->
-        <img src="autocron.php" alt="" width="1" height="1" border="0" />
-        <!-- Do not remove <img src="autocron.php" alt="" width="1" height="1" border="0" /> or your auto cron will cease to function -->
-    </div>
-<?php
-} ?>
-</div>
-</div>
-<div id="overlay"></div>
-<div id="loading">
-    <i class="icon-spinner icon-spin icon-3x pull-left icon-light"></i>
-    <h1>Loading ...</h1>
-</div>
-<div class="dialog" style="display:none;width:650px;" id="popup">
-    <div class="body"></div>
-</div>
-<script type="text/javascript">
-if ($.support.pjax) {
-  $(document).on('click', 'a', function(event) {
-    if (!$(this).hasClass('no-pjax')
-        && !$(this).closest('.no-pjax').length
-        && $(this).attr('href')[0] != '#')
-      $.pjax.click(event, {container: $('#pjax-container'), timeout: 2000});
-  })
-}
-</script>
-</body>
-</html>
-<?php } # endif X_PJAX ?>
-
+</div>
+</div>
+<?php if (!isset($_SERVER['HTTP_X_PJAX'])) { ?>
+    <div id="footer">
+        Copyright &copy; 2006-<?php echo date('Y'); ?>&nbsp;<?php echo (string) $ost->company ?: 'osTicket.com'; ?>&nbsp;All Rights Reserved.
+    </div>
+<?php
+if(is_object($thisstaff) && $thisstaff->isStaff()) { ?>
+    <div>
+        <!-- Do not remove <img src="autocron.php" alt="" width="1" height="1" border="0" /> or your auto cron will cease to function -->
+        <img src="autocron.php" alt="" width="1" height="1" border="0" />
+        <!-- Do not remove <img src="autocron.php" alt="" width="1" height="1" border="0" /> or your auto cron will cease to function -->
+    </div>
+<?php
+} ?>
+</div>
+<div id="overlay"></div>
+<div id="loading">
+    <i class="icon-spinner icon-spin icon-3x pull-left icon-light"></i>
+    <h1><?php echo __('Loading ...');?></h1>
+</div>
+<div class="dialog draggable" style="display:none;width:650px;" id="popup">
+    <div id="popup-loading">
+        <h1 style="margin-bottom: 20px;"><i class="icon-spinner icon-spin icon-large"></i>
+        <?php echo __('Loading ...');?></h1>
+    </div>
+    <div class="body"></div>
+</div>
+<div style="display:none;" class="dialog" id="alert">
+    <h3><i class="icon-warning-sign"></i> <span id="title"></span></h3>
+    <a class="close" href=""><i class="icon-remove-circle"></i></a>
+    <hr/>
+    <div id="body" style="min-height: 20px;"></div>
+    <hr style="margin-top:3em"/>
+    <p class="full-width">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('OK');?>" class="close">
+        </span>
+     </p>
+    <div class="clear"></div>
+</div>
+
+<script type="text/javascript">
+if ($.support.pjax) {
+  $(document).on('click', 'a', function(event) {
+    if (!$(this).hasClass('no-pjax')
+        && !$(this).closest('.no-pjax').length
+        && $(this).attr('href')[0] != '#')
+      $.pjax.click(event, {container: $('#pjax-container'), timeout: 2000});
+  })
+}
+</script>
+<?php
+if ($thisstaff && $thisstaff->getLanguage() != 'en_US') { ?>
+    <script type="text/javascript" src="ajax.php/i18n/<?php
+        echo $thisstaff->getLanguage(); ?>/js"></script>
+<?php } ?>
+</body>
+</html>
+<?php } # endif X_PJAX ?>
diff --git a/include/staff/group.inc.php b/include/staff/group.inc.php
index 5ba2fa6acb3a4a4da71db71b48017a050503965f..27546e764e2bdad1edea44239281a20ac5a312ed 100644
--- a/include/staff/group.inc.php
+++ b/include/staff/group.inc.php
@@ -3,17 +3,17 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access
 $info=array();
 $qstr='';
 if($group && $_REQUEST['a']!='add'){
-    $title='Update Group';
+    $title=__('Update Group');
     $action='update';
-    $submit_text='Save Changes';
+    $submit_text=__('Save Changes');
     $info=$group->getInfo();
     $info['id']=$group->getId();
     $info['depts']=$group->getDepartments();
     $qstr.='&id='.$group->getId();
 }else {
-    $title='Add New Group';
+    $title=__('Add New Group');
     $action='create';
-    $submit_text='Create Group';
+    $submit_text=__('Create Group');
     $info['isactive']=isset($info['isactive'])?$info['isactive']:1;
     $info['can_create_tickets']=isset($info['can_create_tickets'])?$info['can_create_tickets']:1;
     $qstr.='&a='.$_REQUEST['a'];
@@ -25,20 +25,20 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="do" value="<?php echo $action; ?>">
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2>User Group</h2>
+ <h2><?php echo __('Group Access and Permissions');?></h2>
  <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em><strong>Group Information</strong>: Disabled group will limit staff members access. Admins are exempted.</em>
+                <em><strong><?php echo __('Group Information');?></strong>: <?php echo __("Disabled group will limit agents' access. Admins are exempted.");?></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <td width="180" class="required">
-                Name:
+                <?php echo __('Name');?>:
             </td>
             <td>
                 <input type="text" size="30" name="name" value="<?php echo $info['name']; ?>">
@@ -47,116 +47,116 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180" class="required">
-                Status:
+                <?php echo __('Status');?>:
             </td>
             <td>
-                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong>Active</strong>
+                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong><?php echo __('Active');?></strong>
                 &nbsp;
-                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>>Disabled
+                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>><strong><?php echo __('Disabled');?></strong>
                 &nbsp;<span class="error">*&nbsp;<?php echo $errors['status']; ?></span>
                 <i class="help-tip icon-question-sign" href="#status"></i>
             </td>
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Group Permissions</strong>: Applies to all group members&nbsp;</em>
+                <em><strong><?php echo __('Group Permissions');?></strong>: <?php echo __('Applies to all group members');?>&nbsp;</em>
             </th>
         </tr>
-        <tr><td>Can <b>Create</b> Tickets</td>
+        <tr><td><?php echo __('Can <b>Create</b> Tickets');?></td>
             <td>
-                <input type="radio" name="can_create_tickets"  value="1"   <?php echo $info['can_create_tickets']?'checked="checked"':''; ?> />Yes
+                <input type="radio" name="can_create_tickets"  value="1"   <?php echo $info['can_create_tickets']?'checked="checked"':''; ?> /><?php echo __('Yes');?>
                 &nbsp;&nbsp;
-                <input type="radio" name="can_create_tickets"  value="0"   <?php echo !$info['can_create_tickets']?'checked="checked"':''; ?> />No
-                &nbsp;&nbsp;<i>Ability to open tickets on behalf of users.</i>
+                <input type="radio" name="can_create_tickets"  value="0"   <?php echo !$info['can_create_tickets']?'checked="checked"':''; ?> /><?php echo __('No');?>
+                &nbsp;&nbsp;<i><?php echo __('Ability to open tickets on behalf of users.');?></i>
             </td>
         </tr>
-        <tr><td>Can <b>Edit</b> Tickets</td>
+        <tr><td><?php echo __('Can <b>Edit</b> Tickets</td>');?>
             <td>
-                <input type="radio" name="can_edit_tickets"  value="1"   <?php echo $info['can_edit_tickets']?'checked="checked"':''; ?> />Yes
+                <input type="radio" name="can_edit_tickets"  value="1"   <?php echo $info['can_edit_tickets']?'checked="checked"':''; ?> /><?php echo __('Yes');?>
                 &nbsp;&nbsp;
-                <input type="radio" name="can_edit_tickets"  value="0"   <?php echo !$info['can_edit_tickets']?'checked="checked"':''; ?> />No
-                &nbsp;&nbsp;<i>Ability to edit tickets.</i>
+                <input type="radio" name="can_edit_tickets"  value="0"   <?php echo !$info['can_edit_tickets']?'checked="checked"':''; ?> /><?php echo __('No');?>
+                &nbsp;&nbsp;<i><?php echo __('Ability to edit tickets.');?></i>
             </td>
         </tr>
-        <tr><td>Can <b>Post Reply</b></td>
+        <tr><td><?php echo __('Can <b>Post Reply</b>');?></td>
             <td>
-                <input type="radio" name="can_post_ticket_reply"  value="1"   <?php echo $info['can_post_ticket_reply']?'checked="checked"':''; ?> />Yes
+                <input type="radio" name="can_post_ticket_reply"  value="1"   <?php echo $info['can_post_ticket_reply']?'checked="checked"':''; ?> /><?php echo __('Yes');?>
                 &nbsp;&nbsp;
-                <input type="radio" name="can_post_ticket_reply"  value="0"   <?php echo !$info['can_post_ticket_reply']?'checked="checked"':''; ?> />No
-                &nbsp;&nbsp;<i>Ability to post a ticket reply.</i>
+                <input type="radio" name="can_post_ticket_reply"  value="0"   <?php echo !$info['can_post_ticket_reply']?'checked="checked"':''; ?> /><?php echo __('No');?>
+                &nbsp;&nbsp;<i><?php echo __('Ability to post a ticket reply.');?></i>
             </td>
         </tr>
-        <tr><td>Can <b>Close</b> Tickets</td>
+        <tr><td><?php echo __('Can <b>Close</b> Tickets');?></td>
             <td>
-                <input type="radio" name="can_close_tickets"  value="1" <?php echo $info['can_close_tickets']?'checked="checked"':''; ?> />Yes
+                <input type="radio" name="can_close_tickets"  value="1" <?php echo $info['can_close_tickets']?'checked="checked"':''; ?> /><?php echo __('Yes');?>
                 &nbsp;&nbsp;
-                <input type="radio" name="can_close_tickets"  value="0" <?php echo !$info['can_close_tickets']?'checked="checked"':''; ?> />No
-                &nbsp;&nbsp;<i>Ability to close tickets. Staff can still post a response.</i>
+                <input type="radio" name="can_close_tickets"  value="0" <?php echo !$info['can_close_tickets']?'checked="checked"':''; ?> /><?php echo __('No');?>
+                &nbsp;&nbsp;<i><?php echo __('Ability to close tickets.  Agents can still post a response.');?></i>
             </td>
         </tr>
-        <tr><td>Can <b>Assign</b> Tickets</td>
+        <tr><td><?php echo __('Can <b>Assign</b> Tickets');?></td>
             <td>
-                <input type="radio" name="can_assign_tickets"  value="1" <?php echo $info['can_assign_tickets']?'checked="checked"':''; ?> />Yes
+                <input type="radio" name="can_assign_tickets"  value="1" <?php echo $info['can_assign_tickets']?'checked="checked"':''; ?> /><?php echo __('Yes');?>
                 &nbsp;&nbsp;
-                <input type="radio" name="can_assign_tickets"  value="0" <?php echo !$info['can_assign_tickets']?'checked="checked"':''; ?> />No
-                &nbsp;&nbsp;<i>Ability to assign tickets to staff members.</i>
+                <input type="radio" name="can_assign_tickets"  value="0" <?php echo !$info['can_assign_tickets']?'checked="checked"':''; ?> /><?php echo __('No');?>
+                &nbsp;&nbsp;<i><?php echo __('Ability to assign tickets to agents.');?></i>
             </td>
         </tr>
-        <tr><td>Can <b>Transfer</b> Tickets</td>
+        <tr><td><?php echo __('Can <b>Transfer</b> Tickets');?></td>
             <td>
-                <input type="radio" name="can_transfer_tickets"  value="1" <?php echo $info['can_transfer_tickets']?'checked="checked"':''; ?> />Yes
+                <input type="radio" name="can_transfer_tickets"  value="1" <?php echo $info['can_transfer_tickets']?'checked="checked"':''; ?> /><?php echo __('Yes');?>
                 &nbsp;&nbsp;
-                <input type="radio" name="can_transfer_tickets"  value="0" <?php echo !$info['can_transfer_tickets']?'checked="checked"':''; ?> />No
-                &nbsp;&nbsp;<i>Ability to transfer tickets between departments.</i>
+                <input type="radio" name="can_transfer_tickets"  value="0" <?php echo !$info['can_transfer_tickets']?'checked="checked"':''; ?> /><?php echo __('No');?>
+                &nbsp;&nbsp;<i><?php echo __('Ability to transfer tickets between departments.');?></i>
             </td>
         </tr>
-        <tr><td>Can <b>Delete</b> Tickets</td>
+        <tr><td><?php echo __('Can <b>Delete</b> Tickets');?></td>
             <td>
-                <input type="radio" name="can_delete_tickets"  value="1"   <?php echo $info['can_delete_tickets']?'checked="checked"':''; ?> />Yes
+                <input type="radio" name="can_delete_tickets"  value="1"   <?php echo $info['can_delete_tickets']?'checked="checked"':''; ?> /><?php echo __('Yes');?>
                 &nbsp;&nbsp;
-                <input type="radio" name="can_delete_tickets"  value="0"   <?php echo !$info['can_delete_tickets']?'checked="checked"':''; ?> />No
-                &nbsp;&nbsp;<i>Ability to delete tickets (Deleted tickets can't be recovered!)</i>
+                <input type="radio" name="can_delete_tickets"  value="0"   <?php echo !$info['can_delete_tickets']?'checked="checked"':''; ?> /><?php echo __('No');?>
+                &nbsp;&nbsp;<i><?php echo __("Ability to delete tickets (Deleted tickets can't be recovered!)");?></i>
             </td>
         </tr>
-        <tr><td>Can Ban Emails</td>
+        <tr><td><?php echo __('Can Ban Emails');?></td>
             <td>
-                <input type="radio" name="can_ban_emails"  value="1" <?php echo $info['can_ban_emails']?'checked="checked"':''; ?> />Yes
+                <input type="radio" name="can_ban_emails"  value="1" <?php echo $info['can_ban_emails']?'checked="checked"':''; ?> /><?php echo __('Yes');?>
                 &nbsp;&nbsp;
-                <input type="radio" name="can_ban_emails"  value="0" <?php echo !$info['can_ban_emails']?'checked="checked"':''; ?> />No
-                &nbsp;&nbsp;<i>Ability to add/remove emails from banlist via ticket interface.</i>
+                <input type="radio" name="can_ban_emails"  value="0" <?php echo !$info['can_ban_emails']?'checked="checked"':''; ?> /><?php echo __('No');?>
+                &nbsp;&nbsp;<i><?php echo __('Ability to add/remove emails from banlist via ticket interface.');?></i>
             </td>
         </tr>
-        <tr><td>Can Manage Premade</td>
+        <tr><td><?php echo __('Can Manage Premade');?></td>
             <td>
-                <input type="radio" name="can_manage_premade"  value="1" <?php echo $info['can_manage_premade']?'checked="checked"':''; ?> />Yes
+                <input type="radio" name="can_manage_premade"  value="1" <?php echo $info['can_manage_premade']?'checked="checked"':''; ?> /><?php echo __('Yes');?>
                 &nbsp;&nbsp;
-                <input type="radio" name="can_manage_premade"  value="0" <?php echo !$info['can_manage_premade']?'checked="checked"':''; ?> />No
-                &nbsp;&nbsp;<i>Ability to add/update/disable/delete canned responses and attachments.</i>
+                <input type="radio" name="can_manage_premade"  value="0" <?php echo !$info['can_manage_premade']?'checked="checked"':''; ?> /><?php echo __('No');?>
+                &nbsp;&nbsp;<i><?php echo __('Ability to add/update/disable/delete canned responses and attachments.');?></i>
             </td>
         </tr>
-        <tr><td>Can Manage FAQ</td>
+        <tr><td><?php echo __('Can Manage FAQ');?></td>
             <td>
-                <input type="radio" name="can_manage_faq"  value="1" <?php echo $info['can_manage_faq']?'checked="checked"':''; ?> />Yes
+                <input type="radio" name="can_manage_faq"  value="1" <?php echo $info['can_manage_faq']?'checked="checked"':''; ?> /><?php echo __('Yes');?>
                 &nbsp;&nbsp;
-                <input type="radio" name="can_manage_faq"  value="0" <?php echo !$info['can_manage_faq']?'checked="checked"':''; ?> />No
-                &nbsp;&nbsp;<i>Ability to add/update/disable/delete knowledgebase categories and FAQs.</i>
+                <input type="radio" name="can_manage_faq"  value="0" <?php echo !$info['can_manage_faq']?'checked="checked"':''; ?> /><?php echo __('No');?>
+                &nbsp;&nbsp;<i><?php echo __('Ability to add/update/disable/delete knowledgebase categories and FAQs.');?></i>
             </td>
         </tr>
-        <tr><td>Can View Staff Stats.</td>
+        <tr><td><?php echo __('Can View Agent Stats');?></td>
             <td>
-                <input type="radio" name="can_view_staff_stats"  value="1" <?php echo $info['can_view_staff_stats']?'checked="checked"':''; ?> />Yes
+                <input type="radio" name="can_view_staff_stats"  value="1" <?php echo $info['can_view_staff_stats']?'checked="checked"':''; ?> /><?php echo __('Yes');?>
                 &nbsp;&nbsp;
-                <input type="radio" name="can_view_staff_stats"  value="0" <?php echo !$info['can_view_staff_stats']?'checked="checked"':''; ?> />No
-                &nbsp;&nbsp;<i>Ability to view stats of other staff members in allowed departments.</i>
+                <input type="radio" name="can_view_staff_stats"  value="0" <?php echo !$info['can_view_staff_stats']?'checked="checked"':''; ?> /><?php echo __('No');?>
+                &nbsp;&nbsp;<i><?php echo __('Ability to view stats of other agents in allowed departments.');?></i>
             </td>
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Department Access</strong>:
+                <em><strong><?php echo __('Department Access');?></strong>:
                 <i class="help-tip icon-question-sign" href="#department_access"></i>
-                &nbsp;<a id="selectAll" href="#deptckb">Select All</a>
+                &nbsp;<a id="selectAll" href="#deptckb"><?php echo __('Select All');?></a>
                 &nbsp;&nbsp;
-                <a id="selectNone" href="#deptckb">Select None</a></em>
+                <a id="selectNone" href="#deptckb"><?php echo __('Select None');?></a></em>
             </th>
         </tr>
         <?php
@@ -170,7 +170,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         ?>
         <tr>
             <th colspan="2">
-                <em><strong>Admin Notes</strong>: Internal notes viewable by all admins.&nbsp;</em>
+                <em><strong><?php echo __('Admin Notes');?></strong>: <?php echo __('Internal notes viewable by all admins.');?>&nbsp;</em>
             </th>
         </tr>
         <tr>
@@ -183,7 +183,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 </table>
 <p style="text-align:center">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="groups.php"'>
+    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="groups.php"'>
 </p>
 </form>
diff --git a/include/staff/groups.inc.php b/include/staff/groups.inc.php
index 69a18e90d0e79d75c6dbf168ac1eeeb255f24a26..4b47dba87554fceda68666db180f1d2a4fbc30d1 100644
--- a/include/staff/groups.inc.php
+++ b/include/staff/groups.inc.php
@@ -8,7 +8,7 @@ $sql='SELECT grp.*,count(DISTINCT staff.staff_id) as users, count(DISTINCT dept.
      .' LEFT JOIN '.STAFF_TABLE.' staff ON(staff.group_id=grp.group_id) '
      .' LEFT JOIN '.GROUP_DEPT_TABLE.' dept ON(dept.group_id=grp.group_id) '
      .' WHERE 1';
-$sortOptions=array('name'=>'grp.group_name','status'=>'grp.group_enabled', 
+$sortOptions=array('name'=>'grp.group_name','status'=>'grp.group_enabled',
                    'users'=>'users', 'depts'=>'depts', 'created'=>'grp.created','updated'=>'grp.updated');
 $orderWays=array('DESC'=>'DESC','ASC'=>'ASC');
 $sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'name';
@@ -34,18 +34,18 @@ $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
 $query="$sql GROUP BY grp.group_id ORDER BY $order_by";
 $res=db_query($query);
 if($res && ($num=db_num_rows($res)))
-    $showing="Showing 1-$num of $num groups";
+    $showing=sprintf(__('Showing 1-%1$d of %2$d groups'), $num, $num);
 else
-    $showing='No groups found!';
+    $showing=__('No groups found!');
 
 ?>
-<div style="width:700px;padding-top:5px; float:left;">
- <h2>User Groups
+<div class="pull-left" style="width:700px;padding-top:5px;">
+ <h2><?php echo __('Agent Groups');?>
     <i class="help-tip icon-question-sign" href="#groups"></i>
     </h2>
  </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
-    <b><a href="groups.php?a=add" class="Icon newgroup">Add New Group</a></b></div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
+    <b><a href="groups.php?a=add" class="Icon newgroup"><?php echo __('Add New Group');?></a></b></div>
 <div class="clear"></div>
 <form action="groups.php" method="POST" name="groups">
  <?php csrf_token(); ?>
@@ -55,13 +55,13 @@ else
     <caption><?php echo $showing; ?></caption>
     <thead>
         <tr>
-            <th width="7px">&nbsp;</th>        
-            <th width="200"><a <?php echo $name_sort; ?> href="groups.php?<?php echo $qstr; ?>&sort=name">Group Name</a></th>
-            <th width="80"><a  <?php echo $status_sort; ?> href="groups.php?<?php echo $qstr; ?>&sort=status">Status</a></th>
-            <th width="80" style="text-align:center;"><a  <?php echo $users_sort; ?>href="groups.php?<?php echo $qstr; ?>&sort=users">Members</a></th>
-            <th width="80" style="text-align:center;"><a  <?php echo $depts_sort; ?>href="groups.php?<?php echo $qstr; ?>&sort=depts">Departments</a></th>
-            <th width="100"><a  <?php echo $created_sort; ?> href="groups.php?<?php echo $qstr; ?>&sort=created">Created On</a></th>
-            <th width="120"><a  <?php echo $updated_sort; ?> href="groups.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th>
+            <th width="7px">&nbsp;</th>
+            <th width="200"><a <?php echo $name_sort; ?> href="groups.php?<?php echo $qstr; ?>&sort=name"><?php echo __('Group Name');?></a></th>
+            <th width="80"><a  <?php echo $status_sort; ?> href="groups.php?<?php echo $qstr; ?>&sort=status"><?php echo __('Status');?></a></th>
+            <th width="80" style="text-align:center;"><a  <?php echo $users_sort; ?>href="groups.php?<?php echo $qstr; ?>&sort=users"><?php echo __('Members');?></a></th>
+            <th width="80" style="text-align:center;"><a  <?php echo $depts_sort; ?>href="groups.php?<?php echo $qstr; ?>&sort=depts"><?php echo __('Departments');?></a></th>
+            <th width="100"><a  <?php echo $created_sort; ?> href="groups.php?<?php echo $qstr; ?>&sort=created"><?php echo __('Created On');?></a></th>
+            <th width="120"><a  <?php echo $updated_sort; ?> href="groups.php?<?php echo $qstr; ?>&sort=updated"><?php echo __('Last Updated');?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -76,10 +76,10 @@ else
                 ?>
             <tr id="<?php echo $row['group_id']; ?>">
                 <td width=7px>
-                  <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['group_id']; ?>" 
+                  <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['group_id']; ?>"
                             <?php echo $sel?'checked="checked"':''; ?>> </td>
                 <td><a href="groups.php?id=<?php echo $row['group_id']; ?>"><?php echo $row['group_name']; ?></a> &nbsp;</td>
-                <td>&nbsp;<?php echo $row['group_enabled']?'Active':'<b>Disabled</b>'; ?></td>
+                <td>&nbsp;<?php echo $row['group_enabled']?__('Active'):'<b>'.__('Disabled').'</b>'; ?></td>
                 <td style="text-align:right;padding-right:30px">&nbsp;&nbsp;
                     <?php if($row['users']>0) { ?>
                         <a href="staff.php?gid=<?php echo $row['group_id']; ?>"><?php echo $row['users']; ?></a>
@@ -100,12 +100,12 @@ else
      <tr>
         <td colspan="7">
             <?php if($res && $num){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No groups found!';
+                echo __('No groups found!');
             } ?>
         </td>
      </tr>
@@ -115,9 +115,9 @@ else
 if($res && $num): //Show options..
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="enable" value="Enable" >
-    <input class="button" type="submit" name="disable" value="Disable" >
-    <input class="button" type="submit" name="delete" value="Delete">
+    <input class="button" type="submit" name="enable" value="<?php echo __('Enable');?>" >
+    <input class="button" type="submit" name="disable" value="<?php echo __('Disable');?>" >
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete');?>">
 </p>
 <?php
 endif;
@@ -125,27 +125,30 @@ endif;
 </form>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="enable-confirm">
-        Are you sure want to <b>enable</b> selected groups?
+        <?php echo sprintf(__('Are you sure want to <b>enable</b> %s?'),
+            _N('selected group', 'selected groups', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="disable-confirm">
-        Are you sure want to <b>disable</b> selected groups?
+        <?php echo sprintf(__('Are you sure want to <b>disable</b> %s?'),
+            _N('selected group', 'selected groups', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected groups?</strong></font>
-        <br><br>Deleted groups CANNOT be recovered and might affect staff's access.
+        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
+            _N('selected group', 'selected groups', 2));?></strong></font>
+        <br><br><?php echo __("Deleted data CANNOT be recovered and might affect agents' access.");?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/header.inc.php b/include/staff/header.inc.php
index 370a749a32e24d16fff5b5c819adef9589231a32..60079586dfc40c86fb3a3490a1aa8a6cdc0ce97e 100644
--- a/include/staff/header.inc.php
+++ b/include/staff/header.inc.php
@@ -1,13 +1,18 @@
 <?php if (!isset($_SERVER['HTTP_X_PJAX'])) { ?>
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
+<html <?php
+if (($lang = Internationalization::getCurrentLanguage())
+        && ($info = Internationalization::getLanguageInfo($lang))
+        && (@$info['direction'] == 'rtl'))
+    echo 'dir="rtl" class="rtl"';
+?>>
 <head>
     <meta http-equiv="content-type" content="text/html; charset=UTF-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
     <meta http-equiv="cache-control" content="no-cache" />
     <meta http-equiv="pragma" content="no-cache" />
     <meta http-equiv="x-pjax-version" content="<?php echo GIT_VERSION; ?>">
-    <title><?php echo ($ost && ($title=$ost->getPageTitle()))?$title:'osTicket :: Staff Control Panel'; ?></title>
+    <title><?php echo ($ost && ($title=$ost->getPageTitle()))?$title:'osTicket :: '.__('Staff Control Panel'); ?></title>
     <!--[if IE]>
     <style type="text/css">
         .tip_shadow { display:block !important; }
@@ -15,14 +20,15 @@
     <![endif]-->
     <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jquery-1.8.3.min.js"></script>
     <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jquery-ui-1.10.3.custom.min.js"></script>
+    <script type="text/javascript" src="./js/scp.js"></script>
     <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jquery.pjax.js"></script>
-    <script type="text/javascript" src="../js/jquery.multifile.js"></script>
+    <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/filedrop.field.js"></script>
+    <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jquery.multiselect.min.js"></script>
     <script type="text/javascript" src="./js/tips.js"></script>
     <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor.min.js"></script>
     <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor-osticket.js"></script>
     <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor-fonts.js"></script>
     <script type="text/javascript" src="./js/bootstrap-typeahead.js"></script>
-    <script type="text/javascript" src="./js/scp.js"></script>
     <link rel="stylesheet" href="<?php echo ROOT_PATH ?>css/thread.css" media="all">
     <link rel="stylesheet" href="./css/scp.css" media="all">
     <link rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/redactor.css" media="screen">
@@ -35,7 +41,9 @@
     <![endif]-->
     <link type="text/css" rel="stylesheet" href="./css/dropdown.css">
     <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/loadingbar.css"/>
+    <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/rtl.css"/>
     <script type="text/javascript" src="./js/jquery.dropdown.js"></script>
+
     <?php
     if($ost && ($headers=$ost->getExtraHeaders())) {
         echo "\n\t".implode("\n\t", $headers)."\n";
@@ -53,16 +61,16 @@
         echo sprintf('<div id="notice_bar">%s</div>', $ost->getNotice());
     ?>
     <div id="header">
-        <a href="index.php" class="no-pjax" id="logo">osTicket - Customer Support System</a>
-        <p id="info">Welcome, <strong><?php echo $thisstaff->getFirstName(); ?></strong>
+        <a href="index.php" class="no-pjax pull-left" id="logo">osTicket &mdash; <?php echo __('Customer Support System'); ?></a>
+        <p id="info" class="pull-right"><?php echo sprintf(__('Welcome, %s.'), '<strong>'.$thisstaff->getFirstName().'</strong>'); ?>
            <?php
             if($thisstaff->isAdmin() && !defined('ADMINPAGE')) { ?>
-            | <a href="admin.php" class="no-pjax">Admin Panel</a>
+            | <a href="admin.php" class="no-pjax"><?php echo __('Admin Panel'); ?></a>
             <?php }else{ ?>
-            | <a href="index.php" class="no-pjax">Staff Panel</a>
+            | <a href="index.php" class="no-pjax"><?php echo __('Agent Panel'); ?></a>
             <?php } ?>
-            | <a href="profile.php">My Preferences</a>
-            | <a href="logout.php?auth=<?php echo $ost->getLinkToken(); ?>" class="no-pjax">Log Out</a>
+            | <a href="profile.php"><?php echo __('My Preferences'); ?></a>
+            | <a href="logout.php?auth=<?php echo $ost->getLinkToken(); ?>" class="no-pjax"><?php echo __('Log Out'); ?></a>
         </p>
     </div>
     <div id="pjax-container" class="<?php if ($_POST) echo 'no-pjax'; ?>">
@@ -77,7 +85,7 @@
         if (strpos($h, '<script ') !== false)
             echo $h;
     } ?>
-    <title><?php echo ($ost && ($title=$ost->getPageTitle()))?$title:'osTicket :: Staff Control Panel'; ?></title><?php
+    <title><?php echo ($ost && ($title=$ost->getPageTitle()))?$title:'osTicket :: '.__('Staff Control Panel'); ?></title><?php
 } # endif X_PJAX ?>
     <ul id="nav">
 <?php include STAFFINC_DIR . "templates/navigation.tmpl.php"; ?>
diff --git a/include/staff/helptopic.inc.php b/include/staff/helptopic.inc.php
index 00f06d38d0ca604b2adf81daa606132110fd7359..bd254cd2db7e1b7a1bc5d06ac42706cb5b6b3375 100644
--- a/include/staff/helptopic.inc.php
+++ b/include/staff/helptopic.inc.php
@@ -3,17 +3,17 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access
 $info=array();
 $qstr='';
 if($topic && $_REQUEST['a']!='add') {
-    $title='Update Help Topic';
+    $title=__('Update Help Topic');
     $action='update';
-    $submit_text='Save Changes';
+    $submit_text=__('Save Changes');
     $info=$topic->getInfo();
     $info['id']=$topic->getId();
     $info['pid']=$topic->getPid();
     $qstr.='&id='.$topic->getId();
 } else {
-    $title='Add New Help Topic';
+    $title=__('Add New Help Topic');
     $action='create';
-    $submit_text='Add Topic';
+    $submit_text=__('Add Topic');
     $info['isactive']=isset($info['isactive'])?$info['isactive']:1;
     $info['ispublic']=isset($info['ispublic'])?$info['ispublic']:1;
     $info['form_id'] = Topic::FORM_USE_PARENT;
@@ -26,20 +26,21 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="do" value="<?php echo $action; ?>">
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2>Help Topic</h2>
+ <h2><?php echo __('Help Topic');?></h2>
  <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em>Help Topic Information&nbsp;<i class="help-tip icon-question-sign" href="#help_topic_information"></i></em>
+                <em><?php echo __('Help Topic Information');?>
+                &nbsp;<i class="help-tip icon-question-sign" href="#help_topic_information"></i></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <td width="180" class="required">
-               Topic:
+               <?php echo __('Topic');?>:
             </td>
             <td>
                 <input type="text" size="30" name="topic" value="<?php echo $info['topic']; ?>">
@@ -48,31 +49,31 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180" class="required">
-                Status:
+                <?php echo __('Status');?>:
             </td>
             <td>
-                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>>Active
-                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>>Disabled
+                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><?php echo __('Active'); ?>
+                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>><?php echo __('Disabled'); ?>
                 &nbsp;<span class="error">*&nbsp;</span> <i class="help-tip icon-question-sign" href="#status"></i>
             </td>
         </tr>
         <tr>
             <td width="180" class="required">
-                Type:
+                <?php echo __('Type');?>:
             </td>
             <td>
-                <input type="radio" name="ispublic" value="1" <?php echo $info['ispublic']?'checked="checked"':''; ?>>Public
-                <input type="radio" name="ispublic" value="0" <?php echo !$info['ispublic']?'checked="checked"':''; ?>>Private/Internal
+                <input type="radio" name="ispublic" value="1" <?php echo $info['ispublic']?'checked="checked"':''; ?>><?php echo __('Public'); ?>
+                <input type="radio" name="ispublic" value="0" <?php echo !$info['ispublic']?'checked="checked"':''; ?>><?php echo __('Private/Internal'); ?>
                 &nbsp;<span class="error">*&nbsp;</span> <i class="help-tip icon-question-sign" href="#type"></i>
             </td>
         </tr>
         <tr>
             <td width="180">
-                Parent Topic:
+                <?php echo __('Parent Topic');?>:
             </td>
             <td>
                 <select name="topic_pid">
-                    <option value="">&mdash; Top-Level Topic &mdash;</option><?php
+                    <option value="">&mdash; <?php echo __('Top-Level Topic'); ?> &mdash;</option><?php
                     $topics = Topic::getAllHelpTopics();
                     while (list($id,$topic) = each($topics)) {
                         if ($id == $info['topic_id'])
@@ -85,16 +86,16 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             </td>
         </tr>
 
-        <tr><th colspan="2"><em>New ticket options</em></th></tr>
+        <tr><th colspan="2"><em><?php echo __('New ticket options');?></em></th></tr>
         <tr>
-           <td><strong>Custom Form</strong>:</td>
+            <td><strong><?php echo __('Custom Form'); ?></strong>:</td>
            <td><select name="form_id">
                 <option value="0" <?php
 if ($info['form_id'] == '0') echo 'selected="selected"';
-                    ?>>&mdash; None &mdash;</option>
+                    ?>>&mdash; <?php echo __('None'); ?> &mdash;</option>
                 <option value="<?php echo Topic::FORM_USE_PARENT; ?>"  <?php
 if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"';
-                    ?>>&mdash; Use Parent Form &mdash;</option>
+                    ?>>&mdash; <?php echo __('Use Parent Form'); ?> &mdash;</option>
                <?php foreach (DynamicForm::objects()->filter(array('type'=>'G')) as $group) { ?>
                 <option value="<?php echo $group->get('id'); ?>"
                        <?php if ($group->get('id') == $info['form_id'])
@@ -109,11 +110,11 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"';
         </tr>
         <tr>
             <td width="180" class="required">
-                Department:
+                <?php echo __('Department'); ?>:
             </td>
             <td>
                 <select name="dept_id">
-                    <option value="0">&mdash; System Default &mdash;</option>
+                    <option value="0">&mdash; <?php echo __('System Default'); ?> &mdash;</option>
                     <?php
                     $sql='SELECT dept_id,dept_name FROM '.DEPT_TABLE.' dept ORDER by dept_name';
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -130,11 +131,41 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"';
         </tr>
         <tr>
             <td width="180">
-                Priority:
+                <?php echo __('Status'); ?>:
+            </td>
+            <td>
+                <span>
+                <select name="status_id">
+                    <option value="">&mdash; <?php echo __('System Default'); ?> &mdash;</option>
+                    <?php
+                    foreach (TicketStatusList::getStatuses(array('states'=>array('open'))) as $status) {
+                        $name = $status->getName();
+                        if (!($isenabled = $status->isEnabled()))
+                            $name.=' '.__('(disabled)');
+
+                        echo sprintf('<option value="%d" %s %s>%s</option>',
+                                $status->getId(),
+                                ($info['status_id'] == $status->getId())
+                                 ? 'selected="selected"' : '',
+                                 $isenabled ? '' : 'disabled="disabled"',
+                                 $name
+                                );
+                    }
+                    ?>
+                </select>
+                &nbsp;
+                <span class="error"><?php echo $errors['status_id']; ?></span>
+                <i class="help-tip icon-question-sign" href="#status"></i>
+                </span>
+            </td>
+        </tr>
+        <tr>
+            <td width="180">
+                <?php echo __('Priority'); ?>:
             </td>
             <td>
                 <select name="priority_id">
-                    <option value="">&mdash; System Default &mdash;</option>
+                    <option value="">&mdash; <?php echo __('System Default'); ?> &mdash;</option>
                     <?php
                     $sql='SELECT priority_id,priority_desc FROM '.PRIORITY_TABLE.' pri ORDER by priority_urgency DESC';
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -151,11 +182,11 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"';
         </tr>
         <tr>
             <td width="180">
-                SLA Plan:
+                <?php echo __('SLA Plan');?>:
             </td>
             <td>
                 <select name="sla_id">
-                    <option value="0">&mdash; Department's Default &mdash;</option>
+                    <option value="0">&mdash; <?php echo __("Department's Default");?> &mdash;</option>
                     <?php
                     if($slas=SLA::getSLAs()) {
                         foreach($slas as $id =>$name) {
@@ -170,10 +201,10 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"';
             </td>
         </tr>
         <tr>
-            <td width="180">Thank-you Page:</td>
+            <td width="180"><?php echo __('Thank-you Page'); ?>:</td>
             <td>
                 <select name="page_id">
-                    <option value="">&mdash; System Default &mdash;</option>
+                    <option value="">&mdash; <?php echo __('System Default'); ?> &mdash;</option>
                     <?php
                     if(($pages = Page::getActiveThankYouPages())) {
                         foreach($pages as $page) {
@@ -191,14 +222,14 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"';
         </tr>
         <tr>
             <td width="180">
-                Auto-assign To:
+                <?php echo __('Auto-assign To');?>:
             </td>
             <td>
                 <select name="assign">
-                    <option value="0">&mdash; Unassigned &mdash;</option>
+                    <option value="0">&mdash; <?php echo __('Unassigned'); ?> &mdash;</option>
                     <?php
                     if (($users=Staff::getStaffMembers())) {
-                        echo '<OPTGROUP label="Staff Members">';
+                        echo sprintf('<OPTGROUP label="%s">', sprintf(__('Agents (%d)'), count($user)));
                         foreach ($users as $id => $name) {
                             $name = new PersonsName($name);
                             $k="s$id";
@@ -211,14 +242,14 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"';
                         echo '</OPTGROUP>';
                     }
                     $sql='SELECT team_id, name, isenabled FROM '.TEAM_TABLE.' ORDER BY name';
-                    if(($res=db_query($sql)) && db_num_rows($res)){
-                        echo '<OPTGROUP label="Teams">';
+                    if(($res=db_query($sql)) && ($cteams = db_num_rows($res))) {
+                        echo sprintf('<OPTGROUP label="%s">', sprintf(__('Teams (%d)'), $cteams));
                         while (list($id, $name, $isenabled) = db_fetch_row($res)){
                             $k="t$id";
                             $selected = ($info['assign']==$k || $info['team_id']==$id)?'selected="selected"':'';
 
                             if (!$isenabled)
-                                $name .= ' (disabled)';
+                                $name .= ' '.__('(disabled)');
                             ?>
                             <option value="<?php echo $k; ?>"<?php echo $selected; ?>><?php echo $name; ?></option>
                         <?php
@@ -233,17 +264,73 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"';
         </tr>
         <tr>
             <td width="180">
-                Auto-response:
+                <?php echo __('Auto-response'); ?>:
             </td>
             <td>
                 <input type="checkbox" name="noautoresp" value="1" <?php echo $info['noautoresp']?'checked="checked"':''; ?> >
-                    <strong>Disable</strong> new ticket auto-response
+                    <?php echo __('<strong>Disable</strong> new ticket auto-response'); ?>
                     <i class="help-tip icon-question-sign" href="#ticket_auto_response"></i>
             </td>
         </tr>
+        <tr>
+            <td>
+                <?php echo __('Ticket Number Format'); ?>:
+            </td>
+            <td>
+                <label>
+                <input type="radio" name="custom-numbers" value="0" <?php echo !$info['custom-numbers']?'checked="checked"':''; ?>
+                    onchange="javascript:$('#custom-numbers').hide();"> <?php echo __('System Default'); ?>
+                </label>&nbsp;<label>
+                <input type="radio" name="custom-numbers" value="1" <?php echo $info['custom-numbers']?'checked="checked"':''; ?>
+                    onchange="javascript:$('#custom-numbers').show(200);"> <?php echo __('Custom'); ?>
+                </label>&nbsp; <i class="help-tip icon-question-sign" href="#custom_numbers"></i>
+            </td>
+        </tr>
+    </tbody>
+    <tbody id="custom-numbers" style="<?php if (!$info['custom-numbers']) echo 'display:none'; ?>">
+        <tr>
+            <td style="padding-left:20px">
+                <?php echo __('Format'); ?>:
+            </td>
+            <td>
+                <input type="text" name="number_format" value="<?php echo $info['number_format']; ?>"/>
+                <span class="faded"><?php echo __('e.g.'); ?> <span id="format-example"><?php
+                    if ($info['custom-numbers']) {
+                        if ($info['sequence_id'])
+                            $seq = Sequence::lookup($info['sequence_id']);
+                        if (!isset($seq))
+                            $seq = new RandomSequence();
+                        echo $seq->current($info['number_format']);
+                    } ?></span></span>
+                <div class="error"><?php echo $errors['number_format']; ?></div>
+            </td>
+        </tr>
+        <tr>
+<?php $selected = 'selected="selected"'; ?>
+            <td style="padding-left:20px">
+                <?php echo __('Sequence'); ?>:
+            </td>
+            <td>
+                <select name="sequence_id">
+                <option value="0" <?php if ($info['sequence_id'] == 0) echo $selected;
+                    ?>>&mdash; <?php echo __('Random'); ?> &mdash;</option>
+<?php foreach (Sequence::objects() as $s) { ?>
+                <option value="<?php echo $s->id; ?>" <?php
+                    if ($info['sequence_id'] == $s->id) echo $selected;
+                    ?>><?php echo $s->name; ?></option>
+<?php } ?>
+                </select>
+                <button class="action-button pull-right" onclick="javascript:
+                $.dialog('ajax.php/sequence/manage', 205);
+                return false;
+                "><i class="icon-gear"></i> <?php echo __('Manage'); ?></button>
+            </td>
+        </tr>
+    </tbody>
+    <tbody>
         <tr>
             <th colspan="2">
-                <em><strong>Admin Notes</strong>: Internal notes about the help topic.</em>
+                <em><strong><?php echo __('Internal Notes');?></strong>: <?php echo __("be liberal, they're internal.");?></em>
             </th>
         </tr>
         <tr>
@@ -254,9 +341,24 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"';
         </tr>
     </tbody>
 </table>
-<p style="padding-left:225px;">
+<p style="text-align:center;">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="helptopics.php"'>
+    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="helptopics.php"'>
 </p>
 </form>
+<script type="text/javascript">
+$(function() {
+    var request = null,
+      update_example = function() {
+      request && request.abort();
+      request = $.get('ajax.php/sequence/'
+        + $('[name=sequence_id] :selected').val(),
+        {'format': $('[name=number_format]').val()},
+        function(data) { $('#format-example').text(data); }
+      );
+    };
+    $('[name=sequence_id]').on('change', update_example);
+    $('[name=number_format]').on('keyup', update_example);
+});
+</script>
diff --git a/include/staff/helptopics.inc.php b/include/staff/helptopics.inc.php
index 04d3b5238189b00f1d49b7c1e2706cf7c703ac42..d124a67a26f8118829b7758e7310dc42c01b2630 100644
--- a/include/staff/helptopics.inc.php
+++ b/include/staff/helptopics.inc.php
@@ -15,9 +15,9 @@ $page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1;
 $query="$sql ORDER BY $order_by";
 $res=db_query($query);
 if($res && ($num=db_num_rows($res)))
-    $showing="Showing $num help topics";
+    $showing=sprintf(_N('Showing %d help topic', 'Showing %d help topics', $num), $num);
 else
-    $showing='No help topic found!';
+    $showing=__('No help topics found!');
 
 // Get the full names and filter for this page
 $topics = array();
@@ -31,20 +31,20 @@ if ($cfg->getTopicSortMode() == 'a')
     usort($topics, function($a, $b) { return strcmp($a['name'], $b['name']); });
 
 ?>
-<div style="width:700px;padding-top:5px; float:left;">
- <h2>Help Topics</h2>
+<div class="pull-left" style="width:700px;padding-top:5px;">
+ <h2><?php echo __('Help Topics');?></h2>
  </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
-    <b><a href="helptopics.php?a=add" class="Icon newHelpTopic">Add New Help Topic</a></b></div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
+    <b><a href="helptopics.php?a=add" class="Icon newHelpTopic"><?php echo __('Add New Help Topic');?></a></b></div>
 <div class="clear"></div>
 <form action="helptopics.php" method="POST" name="topics">
  <?php csrf_token(); ?>
  <input type="hidden" name="do" value="mass_process" >
 <input type="hidden" id="action" name="a" value="sort" >
  <table class="list" border="0" cellspacing="1" cellpadding="0" width="940">
-    <caption><span style="display:inline-block;vertical-align:middle"><?php
+    <caption><span class="pull-left" style="display:inline-block;vertical-align:middle"><?php
          echo $showing; ?></span>
-    <div class="pull-right">Sorting Mode:
+         <div class="pull-right"><?php echo __('Sorting Mode'); ?>:
         <select name="help_topic_sort_mode" onchange="javascript:
     var $form = $(this).closest('form');
     $form.find('input[name=a]').val('sort');
@@ -59,12 +59,12 @@ if ($cfg->getTopicSortMode() == 'a')
     <thead>
         <tr>
             <th width="7" style="height:20px;">&nbsp;</th>
-            <th style="padding-left:4px;vertical-align:middle" width="360">Help Topic</th>
-            <th style="padding-left:4px;vertical-align:middle" width="80">Status</th>
-            <th style="padding-left:4px;vertical-align:middle" width="100">Type</th>
-            <th style="padding-left:4px;vertical-align:middle" width="100">Priority</th>
-            <th style="padding-left:4px;vertical-align:middle" width="160">Department</th>
-            <th style="padding-left:4px;vertical-align:middle" width="150" nowrap>Last Updated</th>
+            <th style="padding-left:4px;vertical-align:middle" width="360"><?php echo __('Help Topic'); ?></th>
+            <th style="padding-left:4px;vertical-align:middle" width="80"><?php echo __('Status'); ?></th>
+            <th style="padding-left:4px;vertical-align:middle" width="100"><?php echo __('Type'); ?></th>
+            <th style="padding-left:4px;vertical-align:middle" width="100"><?php echo __('Priority'); ?></th>
+            <th style="padding-left:4px;vertical-align:middle" width="160"><?php echo __('Department'); ?></th>
+            <th style="padding-left:4px;vertical-align:middle" width="150" nowrap><?php echo __('Last Updated'); ?></th>
         </tr>
     </thead>
     <tbody class="<?php if ($cfg->getTopicSortMode() == 'm') echo 'sortable-rows'; ?>"
@@ -103,8 +103,8 @@ if ($cfg->getTopicSortMode() == 'a')
                     <i class="icon-sort"></i>
 <?php } ?>
 <a href="helptopics.php?id=<?php echo $row['topic_id']; ?>"><?php echo $row['name']; ?></a>&nbsp;</td>
-                <td><?php echo $row['isactive']?'Active':'<b>Disabled</b>'; ?></td>
-                <td><?php echo $row['ispublic']?'Public':'<b>Private</b>'; ?></td>
+                <td><?php echo $row['isactive']?__('Active'):'<b>'.__('Disabled').'</b>'; ?></td>
+                <td><?php echo $row['ispublic']?__('Public'):'<b>'.__('Private').'</b>'; ?></td>
                 <td><?php echo $row['priority']; ?></td>
                 <td><a href="departments.php?id=<?php echo $row['dept_id']; ?>"><?php echo $row['department']; ?></a></td>
                 <td>&nbsp;<?php echo Format::db_datetime($row['updated']); ?></td>
@@ -116,12 +116,12 @@ if ($cfg->getTopicSortMode() == 'a')
      <tr>
         <td colspan="7">
             <?php if($res && $num){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No help topics found';
+                echo __('No help topics found');
             } ?>
         </td>
      </tr>
@@ -134,9 +134,9 @@ if($res && $num): //Show options..
 <?php if ($cfg->getTopicSortMode() != 'a') { ?>
     <input class="button no-confirm" type="submit" name="sort" value="Save"/>
 <?php } ?>
-    <button class="button" type="submit" name="enable" value="Enable" >Enable</button>
-    <button class="button" type="submit" name="disable" value="Disable">Disable</button>
-    <button class="button" type="submit" name="delete" value="Delete">Delete</button>
+    <button class="button" type="submit" name="enable" value="Enable" ><?php echo __('Enable'); ?></button>
+    <button class="button" type="submit" name="disable" value="Disable"><?php echo __('Disable'); ?></button>
+    <button class="button" type="submit" name="delete" value="Delete"><?php echo __('Delete'); ?></button>
 </p>
 <?php
 endif;
@@ -144,27 +144,30 @@ endif;
 </form>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="enable-confirm">
-        Are you sure want to <b>enable</b> selected help topics?
+        <?php echo sprintf(__('Are you sure want to <b>enable</b> %s?'),
+            _N('selected help topic', 'selected help topics', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="disable-confirm">
-        Are you sure want to <b>disable</b>  selected help topics?
+        <?php echo sprintf(__('Are you sure want to <b>disable</b> %s?'),
+            _N('selected help topic', 'selected help topics', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected help topics?</strong></font>
-        <br><br>Deleted topics CANNOT be recovered.
+        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
+            _N('selected help topic', 'selected help topics', 2));?></strong></font>
+        <br><br><?php echo __('Deleted data CANNOT be recovered.');?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/login.header.php b/include/staff/login.header.php
index 7fecca1735d3196074a756d3cac68f2e8889ce01..7cf18d895239f5a9425428cb112d6a1aba1827a1 100644
--- a/include/staff/login.header.php
+++ b/include/staff/login.header.php
@@ -6,7 +6,7 @@ defined('OSTSCPINC') or die('Invalid path');
 <head>
     <meta http-equiv="content-type" content="text/html; charset=utf-8" />
     <meta http-equiv="refresh" content="7200" />
-    <title>osTicket:: SCP Login</title>
+    <title>osTicket :: <?php echo __('Agent Login'); ?></title>
     <link rel="stylesheet" href="css/login.css" type="text/css" />
     <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/font-awesome.min.css">
     <meta name="robots" content="noindex" />
diff --git a/include/staff/login.tpl.php b/include/staff/login.tpl.php
index 9abf430f39b21bd340f6e39a1002c27ac1e3893f..eb2bf80d05ea1a126b0975fd671719d797c097fa 100644
--- a/include/staff/login.tpl.php
+++ b/include/staff/login.tpl.php
@@ -3,19 +3,19 @@ include_once(INCLUDE_DIR.'staff/login.header.php');
 $info = ($_POST && $errors)?Format::htmlchars($_POST):array();
 ?>
 <div id="loginBox">
-    <h1 id="logo"><a href="index.php">osTicket Staff Control Panel</a></h1>
+    <h1 id="logo"><a href="index.php">osTicket :: <?php echo __('Staff Control Panel');?></a></h1>
     <h3><?php echo Format::htmlchars($msg); ?></h3>
     <div class="banner"><small><?php echo ($content) ? Format::display($content->getBody()) : ''; ?></small></div>
     <form action="login.php" method="post">
         <?php csrf_token(); ?>
         <input type="hidden" name="do" value="scplogin">
         <fieldset>
-            <input type="text" name="userid" id="name" value="<?php echo $info['userid']; ?>" placeholder="username" autocorrect="off" autocapitalize="off">
-            <input type="password" name="passwd" id="pass" placeholder="password" autocorrect="off" autocapitalize="off">
+        <input type="text" name="userid" id="name" value="<?php echo $info['userid']; ?>" placeholder="<?php echo __('Email or Username'); ?>" autocorrect="off" autocapitalize="off">
+        <input type="password" name="passwd" id="pass" placeholder="<?php echo __('Password'); ?>" autocorrect="off" autocapitalize="off">
             <?php if ($show_reset && $cfg->allowPasswordReset()) { ?>
-            <h3 style="display:inline"><a href="pwreset.php">Forgot my password</a></h3>
+            <h3 style="display:inline"><a href="pwreset.php"><?php echo __('Forgot my password'); ?></a></h3>
             <?php } ?>
-            <input class="submit" type="submit" name="submit" value="Log In">
+            <input class="submit" type="submit" name="submit" value="<?php echo __('Log In'); ?>">
         </fieldset>
     </form>
 <?php
@@ -33,6 +33,7 @@ if (count($ext_bks)) { ?>
     }
 } ?>
 </div>
-<div id="copyRights">Copyright &copy; <a href='http://www.osticket.com' target="_blank">osTicket.com</a></div>
+<div id="copyRights"><?php echo __('Copyright'); ?> &copy;
+<a href='http://www.osticket.com' target="_blank">osTicket.com</a></div>
 </body>
 </html>
diff --git a/include/staff/org-view.inc.php b/include/staff/org-view.inc.php
index b893c9f3fd495b03c241d9b3744844fa62d0a8b5..c5cea44eb18989a13f3a3060f9c704183e383a64 100644
--- a/include/staff/org-view.inc.php
+++ b/include/staff/org-view.inc.php
@@ -9,19 +9,21 @@ if(!defined('OSTSCPINC') || !$thisstaff || !is_object($org)) die('Invalid path')
              title="Reload"><i class="icon-refresh"></i> <?php echo $org->getName(); ?></a></h2>
         </td>
         <td width="50%" class="right_align has_bottom_border">
-            <span class="action-button" data-dropdown="#action-dropdown-more">
-                <span ><i class="icon-cog"></i> More</span>
-                <i class="icon-caret-down"></i>
+            <span class="action-button pull-right" data-dropdown="#action-dropdown-more">
+                <i class="icon-caret-down pull-right"></i>
+                <span ><i class="icon-cog"></i> <?php echo __('More'); ?></span>
             </span>
-            <a id="org-delete" class="action-button org-action"
-            href="#orgs/<?php echo $org->getId(); ?>/delete"><i class="icon-trash"></i> Delete Organization</a>
+            <a id="org-delete" class="action-button pull-right org-action"
+            href="#orgs/<?php echo $org->getId(); ?>/delete"><i class="icon-trash"></i>
+            <?php echo __('Delete Organization'); ?></a>
             <div id="action-dropdown-more" class="action-dropdown anchor-right">
               <ul>
                 <li><a href="#ajax.php/orgs/<?php echo $org->getId();
                     ?>/forms/manage" onclick="javascript:
                     $.dialog($(this).attr('href').substr(1), 201);
                     return false"
-                    ><i class="icon-paste"></i> Manage Forms</a></li>
+                    ><i class="icon-paste"></i>
+                    <?php echo __('Manage Forms'); ?></a></li>
               </ul>
             </div>
         </td>
@@ -32,7 +34,7 @@ if(!defined('OSTSCPINC') || !$thisstaff || !is_object($org)) die('Invalid path')
         <td width="50%">
             <table border="0" cellspacing="" cellpadding="4" width="100%">
                 <tr>
-                    <th width="150">Name:</th>
+                    <th width="150"><?php echo __('Name'); ?>:</th>
                     <td><b><a href="#orgs/<?php echo $org->getId();
                     ?>/edit" class="org-action"><i
                     class="icon-edit"></i>&nbsp;<?php echo
@@ -40,7 +42,7 @@ if(!defined('OSTSCPINC') || !$thisstaff || !is_object($org)) die('Invalid path')
                     ?></a></td>
                 </tr>
                 <tr>
-                    <th>Account Manager:</th>
+                    <th><?php echo __('Account Manager'); ?>:</th>
                     <td><?php echo $org->getAccountManager(); ?>&nbsp;</td>
                 </tr>
             </table>
@@ -48,11 +50,11 @@ if(!defined('OSTSCPINC') || !$thisstaff || !is_object($org)) die('Invalid path')
         <td width="50%" style="vertical-align:top">
             <table border="0" cellspacing="" cellpadding="4" width="100%">
                 <tr>
-                    <th width="150">Created:</th>
+                    <th width="150"><?php echo __('Created'); ?>:</th>
                     <td><?php echo Format::db_datetime($org->getCreateDate()); ?></td>
                 </tr>
                 <tr>
-                    <th>Updated:</th>
+                    <th><?php echo __('Last Updated'); ?>:</th>
                     <td><?php echo Format::db_datetime($org->getUpdateDate()); ?></td>
                 </tr>
             </table>
@@ -63,11 +65,11 @@ if(!defined('OSTSCPINC') || !$thisstaff || !is_object($org)) die('Invalid path')
 <div class="clear"></div>
 <ul class="tabs">
     <li><a class="active" id="users_tab" href="#users"><i
-    class="icon-user"></i>&nbsp;Users</a></li>
+    class="icon-user"></i>&nbsp;<?php echo __('Users'); ?></a></li>
     <li><a id="tickets_tab" href="#tickets"><i
-    class="icon-list-alt"></i>&nbsp;Tickets</a></li>
+    class="icon-list-alt"></i>&nbsp;<?php echo __('Tickets'); ?></a></li>
     <li><a id="notes_tab" href="#notes"><i
-    class="icon-pushpin"></i>&nbsp;Notes</a></li>
+    class="icon-pushpin"></i>&nbsp;<?php echo __('Notes'); ?></a></li>
 </ul>
 <div class="tab_content" id="users">
 <?php
diff --git a/include/staff/orgs.inc.php b/include/staff/orgs.inc.php
index 3da261f3978107a1c6df6da02e91181a589a44e1..862da34be7722441fb1b6f1c447edf3ec8b0d868 100644
--- a/include/staff/orgs.inc.php
+++ b/include/staff/orgs.inc.php
@@ -70,8 +70,8 @@ $query="$select $from $where GROUP BY org.id ORDER BY $order_by LIMIT ".$pageNav
 $qhash = md5($query);
 $_SESSION['orgs_qs_'.$qhash] = $query;
 ?>
-<h2>Organizations</h2>
-<div style="width:700px; float:left;">
+<h2><?php echo __('Organizations'); ?></h2>
+<div class="pull-left" style="width:700px;">
     <form action="orgs.php" method="get">
         <?php csrf_token(); ?>
         <input type="hidden" name="a" value="search">
@@ -79,22 +79,23 @@ $_SESSION['orgs_qs_'.$qhash] = $query;
             <tr>
                 <td><input type="text" id="basic-org-search" name="query" size=30 value="<?php echo Format::htmlchars($_REQUEST['query']); ?>"
                 autocomplete="off" autocorrect="off" autocapitalize="off"></td>
-                <td><input type="submit" name="basic_search" class="button" value="Search"></td>
+                <td><input type="submit" name="basic_search" class="button" value="<?php echo __('Search'); ?>"></td>
                 <!-- <td>&nbsp;&nbsp;<a href="" id="advanced-user-search">[advanced]</a></td> -->
             </tr>
         </table>
     </form>
  </div>
- <div style="float:right;text-align:right;padding-right:5px;">
-    <b><a href="#orgs/add" class="Icon newDepartment add-org">Add New Organization</a></b></div>
+ <div class="pull-right flush-right">
+    <b><a href="#orgs/add" class="Icon newDepartment add-org"><?php
+    echo __('Add New Organization'); ?></a></b></div>
 <div class="clear"></div>
 <?php
-$showing = $search ? 'Search Results: ' : '';
+$showing = $search ? __('Search Results').': ' : '';
 $res = db_query($query);
 if($res && ($num=db_num_rows($res)))
     $showing .= $pageNav->showing();
 else
-    $showing .= 'No organizations found!';
+    $showing .= __('No organizations found!');
 ?>
 <form action="orgs.php" method="POST" name="staff" >
  <?php csrf_token(); ?>
@@ -104,10 +105,10 @@ else
     <caption><?php echo $showing; ?></caption>
     <thead>
         <tr>
-            <th width="400"><a <?php echo $name_sort; ?> href="orgs.php?<?php echo $qstr; ?>&sort=name">Name</a></th>
-            <th width="100"><a <?php echo $users_sort; ?> href="orgs.php?<?php echo $qstr; ?>&sort=users">Users</a></th>
-            <th width="150"><a <?php echo $create_sort; ?> href="orgs.php?<?php echo $qstr; ?>&sort=create">Created</a></th>
-            <th width="145"><a <?php echo $update_sort; ?> href="orgs.php?<?php echo $qstr; ?>&sort=update">Updated</a></th>
+            <th width="400"><a <?php echo $name_sort; ?> href="orgs.php?<?php echo $qstr; ?>&sort=name"><?php echo __('Name'); ?></a></th>
+            <th width="100"><a <?php echo $users_sort; ?> href="orgs.php?<?php echo $qstr; ?>&sort=users"><?php echo __('Users'); ?></a></th>
+            <th width="150"><a <?php echo $create_sort; ?> href="orgs.php?<?php echo $qstr; ?>&sort=create"><?php echo __('Created'); ?></a></th>
+            <th width="145"><a <?php echo $update_sort; ?> href="orgs.php?<?php echo $qstr; ?>&sort=update"><?php echo __('Last Updated'); ?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -133,10 +134,12 @@ else
 </table>
 <?php
 if($res && $num): //Show options..
-    echo sprintf('<div>&nbsp;Page: %s &nbsp; <a class="no-pjax"
-            href="orgs.php?a=export&qh=%s">Export</a></div>',
+    echo sprintf('<div>&nbsp;%s: %s &nbsp; <a class="no-pjax"
+            href="orgs.php?a=export&qh=%s">%s</a></div>',
+            __('Page'),
             $pageNav->getPageLinks(),
-            $qhash);
+            $qhash,
+            __('Export'));
 endif;
 ?>
 </form>
diff --git a/include/staff/page.inc.php b/include/staff/page.inc.php
index e4304c38b249c440d324d21c29fb0205790ad93e..532bfc0af8c4c9b9435b9be741ec01c433e2f3c9 100644
--- a/include/staff/page.inc.php
+++ b/include/staff/page.inc.php
@@ -1,26 +1,26 @@
 <?php
 if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied');
 $pageTypes = array(
-        'landing' => 'Landing page',
-        'offline' => 'Offline page',
-        'thank-you' => 'Thank you page',
-        'other' => 'Other',
+        'landing' => __('Landing page'),
+        'offline' => __('Offline page'),
+        'thank-you' => __('Thank you page'),
+        'other' => __('Other'),
         );
 $info=array();
 $qstr='';
 if($page && $_REQUEST['a']!='add'){
-    $title='Update Page';
+    $title=__('Update Page');
     $action='update';
-    $submit_text='Save Changes';
+    $submit_text=__('Save Changes');
     $info=$page->getHashtable();
     $info['body'] = Format::viewableImages($page->getBody());
     $info['notes'] = Format::viewableImages($info['notes']);
     $slug = Format::slugify($info['name']);
     $qstr.='&id='.$page->getId();
 }else {
-    $title='Add New Page';
+    $title=__('Add New Page');
     $action='add';
-    $submit_text='Add Page';
+    $submit_text=__('Add Page');
     $info['isactive']=isset($info['isactive'])?$info['isactive']:0;
     $qstr.='&a='.urlencode($_REQUEST['a']);
 }
@@ -31,7 +31,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="do" value="<?php echo $action; ?>">
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2>Site Pages
+ <h2><?php echo __('Site Pages'); ?>
     <i class="help-tip icon-question-sign" href="#site_pages"></i>
     </h2>
  <table class="form_table fixed" width="940" border="0" cellspacing="0" cellpadding="2">
@@ -40,14 +40,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em>Page information.</em>
+                <em><?php echo __('Page information'); ?></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <td width="180" class="required">
-              Name:
+              <?php echo __('Name'); ?>:
             </td>
             <td>
                 <input type="text" size="40" name="name" value="<?php echo $info['name']; ?>">
@@ -56,12 +56,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180" class="required">
-                Type:
+                <?php echo __('Type'); ?>:
             </td>
             <td>
                 <span>
                 <select name="type">
-                    <option value="" selected="selected">&mdash; Select Page Type &mdash;</option>
+                    <option value="" selected="selected">&mdash; <?php
+                    echo __('Select Page Type'); ?> &mdash;</option>
                     <?php
                     foreach($pageTypes as $k => $v)
                         echo sprintf('<option value="%s" %s>%s</option>',
@@ -76,7 +77,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <?php if ($info['name'] && $info['type'] == 'other') { ?>
         <tr>
             <td width="180" class="required">
-                Public URL:
+                <?php echo __('Public URL'); ?>:
             </td>
             <td><a href="<?php echo sprintf("%s/pages/%s",
                     $ost->getConfig()->getBaseUrl(), urlencode($slug));
@@ -86,17 +87,19 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <?php } ?>
         <tr>
             <td width="180" class="required">
-                Status:
+                <?php echo __('Status'); ?>:
             </td>
             <td>
-                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong>Active</strong>
-                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>>Disabled
+                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong><?php echo __('Active'); ?></strong>
+                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>><?php echo __('Disabled'); ?>
                 &nbsp;<span class="error">*&nbsp;<?php echo $errors['isactive']; ?></span>
             </td>
         </tr>
         <tr>
             <th colspan="2">
-                <em><b>Page body</b>: Ticket variables are only supported in thank-you pages.<font class="error">*&nbsp;<?php echo $errors['body']; ?></font></em>
+                <em><?php echo __(
+                '<b>Page body</b>: Ticket variables are only supported in thank-you pages.'
+                ); ?><font class="error">*&nbsp;<?php echo $errors['body']; ?></font></em>
             </th>
         </tr>
          <tr>
@@ -108,7 +111,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Admin Notes</strong>: Internal notes.&nbsp;</em>
+                <em><strong><?php echo __('Internal Notes'); ?></strong>: 
+                <?php echo __("be liberal, they're internal"); ?></em>
             </th>
         </tr>
         <tr>
@@ -121,7 +125,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 </table>
 <p style="padding-left:225px;">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="pages.php"'>
+    <input type="reset"  name="reset"  value="<?php echo __('Reset'); ?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel'); ?>" onclick='window.location.href="pages.php"'>
 </p>
 </form>
diff --git a/include/staff/pages.inc.php b/include/staff/pages.inc.php
index 8c2692eac9871d4836d660f98baed4c8e6ac6935..19a307f93e2ec9d30fbb94d448f463c5e2e9b438 100644
--- a/include/staff/pages.inc.php
+++ b/include/staff/pages.inc.php
@@ -42,19 +42,19 @@ $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
 $query="$sql $where GROUP BY page.id ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
 $res=db_query($query);
 if($res && ($num=db_num_rows($res)))
-    $showing=$pageNav->showing();
+    $showing=$pageNav->showing()._N('site page','site pages', $num);
 else
-    $showing='No pages found!';
+    $showing=__('No pages found!');
 
 ?>
 
-<div style="width:700px;padding-top:5px; float:left;">
- <h2>Site Pages
+<div class="pull-left" style="width:700px;padding-top:5px;">
+ <h2><?php echo __('Site Pages'); ?>
     <i class="help-tip icon-question-sign" href="#site_pages"></i>
     </h2>
 </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
- <b><a href="pages.php?a=add" class="Icon newPage">Add New Page</a></b></div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
+ <b><a href="pages.php?a=add" class="Icon newPage"><?php echo __('Add New Page'); ?></a></b></div>
 <div class="clear"></div>
 <form action="pages.php" method="POST" name="tpls">
  <?php csrf_token(); ?>
@@ -65,11 +65,11 @@ else
     <thead>
         <tr>
             <th width="7">&nbsp;</th>
-            <th width="300"><a <?php echo $name_sort; ?> href="pages.php?<?php echo $qstr; ?>&sort=name">Name</a></th>
-            <th width="90"><a  <?php echo $type_sort; ?> href="pages.php?<?php echo $qstr; ?>&sort=type">Type</a></th>
-            <th width="110"><a  <?php echo $status_sort; ?> href="pages.php?<?php echo $qstr; ?>&sort=status">Status</a></th>
-            <th width="150" nowrap><a  <?php echo $created_sort; ?>href="pages.php?<?php echo $qstr; ?>&sort=created">Date Added</a></th>
-            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="pages.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th>
+            <th width="300"><a <?php echo $name_sort; ?> href="pages.php?<?php echo $qstr; ?>&sort=name"><?php echo __('Name'); ?></a></th>
+            <th width="90"><a  <?php echo $type_sort; ?> href="pages.php?<?php echo $qstr; ?>&sort=type"><?php echo __('Type'); ?></a></th>
+            <th width="110"><a  <?php echo $status_sort; ?> href="pages.php?<?php echo $qstr; ?>&sort=status"><?php echo __('Status'); ?></a></th>
+            <th width="150" nowrap><a  <?php echo $created_sort; ?>href="pages.php?<?php echo $qstr; ?>&sort=created"><?php echo __('Date Added'); ?></a></th>
+            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="pages.php?<?php echo $qstr; ?>&sort=updated"><?php echo __('Last Updated'); ?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -92,8 +92,8 @@ else
                 <td>&nbsp;<a href="pages.php?id=<?php echo $row['id']; ?>"><?php echo Format::htmlchars($row['name']); ?></a></td>
                 <td class="faded"><?php echo $row['type']; ?></td>
                 <td>
-                    &nbsp;<?php echo $row['isactive']?'Active':'<b>Disabled</b>'; ?>
-                    &nbsp;&nbsp;<?php echo $inuse?'<em>(in-use)</em>':''; ?>
+                    &nbsp;<?php echo $row['isactive']?__('Active'):'<b>'.__('Disabled').'</b>'; ?>
+                    &nbsp;&nbsp;<?php echo $inuse?'<em>'.__('(in-use)').'</em>':''; ?>
                 </td>
                 <td>&nbsp;<?php echo Format::db_date($row['created']); ?></td>
                 <td>&nbsp;<?php echo Format::db_datetime($row['updated']); ?></td>
@@ -105,12 +105,12 @@ else
      <tr>
         <td colspan="6">
             <?php if($res && $num){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select'); ?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All'); ?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None'); ?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle'); ?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No pages found';
+                echo __('No pages found!');
             } ?>
         </td>
      </tr>
@@ -118,12 +118,12 @@ else
 </table>
 <?php
 if($res && $num): //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="enable" value="Enable" >
-    <input class="button" type="submit" name="disable" value="Disable" >
-    <input class="button" type="submit" name="delete" value="Delete" >
+    <input class="button" type="submit" name="enable" value="<?php echo __('Enable'); ?>" >
+    <input class="button" type="submit" name="disable" value="<?php echo __('Disable'); ?>" >
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete'); ?>" >
 </p>
 <?php
 endif;
@@ -131,26 +131,30 @@ endif;
 </form>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm'); ?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="enable-confirm">
-        Are you sure want to <b>enable</b> selected pages?
+        <?php echo sprintf(__('Are you sure want to <b>enable</b> %s?'),
+            _N('selected site page', 'selected site pages', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="disable-confirm">
-        Are you sure want to <b>disable</b>  selected pages?
+        <?php echo sprintf(__('Are you sure want to <b>disable</b> %s?'),
+            _N('selected site page', 'selected site pages', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected pages?</strong></font>
-        <br><br>Deleted pages CANNOT be recovered.
+        <font color="red"><strong><?php echo sprintf(
+        __('Are you sure you want to DELETE %s?'),
+        _N('selected site page', 'selected site pages', 2));?></strong></font>
+        <br><br><?php echo __('Deleted data CANNOT be recovered.'); ?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.'); ?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
+        <span class="buttons pull-left">
             <input type="button" value="No, Cancel" class="close">
         </span>
-        <span class="buttons" style="float:right">
+        <span class="buttons pull-right">
             <input type="button" value="Yes, Do it!" class="confirm">
         </span>
      </p>
diff --git a/include/staff/plugin-add.inc.php b/include/staff/plugin-add.inc.php
index 46251f66d1c2269e8c2417ed0e238613d1e1f8e6..a5aae19d0581e1dce739ef3d05096f7d49028016 100644
--- a/include/staff/plugin-add.inc.php
+++ b/include/staff/plugin-add.inc.php
@@ -1,9 +1,8 @@
 
-<h2>Install a new plugin</h2>
-<p>
-To add a plugin into the system, download and place the plugin into the
-<code>include/plugins</code> folder. Once in the plugin is in the
-<code>plugins/</code> folder, it will be shown in the list below.
+<h2><?php echo __('Install a new plugin'); ?></h2>
+<p><?php echo __(
+'To add a plugin into the system, download and place the plugin into the <code>include/plugins</code> folder. Once in the plugin is in the <code>plugins/</code> folder, it will be shown in the list below.'
+); ?>
 </p>
 
 <form method="post" action="?">
@@ -20,12 +19,12 @@ foreach ($ost->plugins->allInfos() as $info) {
     ?>
         <tr><td><button type="submit" name="install_path"
             value="<?php echo $info['install_path'];
-            ?>">Install</button></td>
+            ?>"><?php echo __('Install'); ?></button></td>
         <td>
     <div><strong><?php echo $info['name']; ?></strong><br/>
         <div><?php echo $info['description']; ?></div>
-        <div class="faded"><em>Version: <?php echo $info['version']; ?></em></div>
-        <div class="faded"><em>Author: <?php echo $info['author']; ?></em></div>
+        <div class="faded"><em><?php echo __('Version'); ?>: <?php echo $info['version']; ?></em></div>
+        <div class="faded"><em><?php echo __('Author'); ?>: <?php echo $info['author']; ?></em></div>
     </div>
     </td></tr>
     <?php
diff --git a/include/staff/plugin.inc.php b/include/staff/plugin.inc.php
index c1cefd5bc2fb60e1570b97e7fec9e6b838461bf9..f76b0f27c749e889b0a8170875798a3d2f932292 100644
--- a/include/staff/plugin.inc.php
+++ b/include/staff/plugin.inc.php
@@ -3,13 +3,15 @@
 $info=array();
 if($plugin && $_REQUEST['a']!='add') {
     $config = $plugin->getConfig();
-    if ($config)
-        $form = $config->getForm();
-    if ($_POST)
-        $form->isValid();
-    $title = 'Update Plugin';
+    if (!($page = $config->hasCustomConfig())) {
+        if ($config)
+            $form = $config->getForm();
+        if ($form && $_POST)
+            $form->isValid();
+    }
+    $title = __('Update Plugin');
     $action = 'update';
-    $submit_text='Save Changes';
+    $submit_text = __('Save Changes');
     $info = $plugin->ht;
 }
 $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
@@ -19,26 +21,30 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
     <?php csrf_token(); ?>
     <input type="hidden" name="do" value="<?php echo $action; ?>">
     <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
-    <h2>Manage Plugin
+    <h2><?php echo __('Manage Plugin'); ?>
         <br/><small><?php echo $plugin->getName(); ?></small></h2>
 
-    <h3>Configuration</h3>
+    <h3><?php echo __('Configuration'); ?></h3>
+<?php
+if ($page)
+    $config->renderCustomConfig();
+elseif ($form) { ?>
     <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
     <tbody>
+<?php $form->render(); ?>
+    </tbody></table>
 <?php
-if ($form)
-    $form->render();
+}
 else { ?>
-    <tr><th>This plugin has no configurable settings<br>
-        <em>Every plugin should be so easy to use</em></th></tr>
+    <tr><th><?php echo __('This plugin has no configurable settings'); ?><br>
+        <em><?php echo __('Every plugin should be so easy to use.'); ?></em></th></tr>
 <?php }
 ?>
-    </tbody></table>
 <p class="centered">
-<?php if ($form) { ?>
+<?php if ($page || $form) { ?>
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
+    <input type="reset"  name="reset"  value="<?php echo __('Reset'); ?>">
 <?php } ?>
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="?"'>
+    <input type="button" name="cancel" value="<?php echo __('Cancel'); ?>" onclick='window.location.href="?"'>
 </p>
 </form>
diff --git a/include/staff/plugins.inc.php b/include/staff/plugins.inc.php
index a25ccc095a1c11a1544d2c24d3666d185ee0ca70..cd3282e4b030ad9ff728ccc916231c9083cb0d0b 100644
--- a/include/staff/plugins.inc.php
+++ b/include/staff/plugins.inc.php
@@ -1,8 +1,9 @@
-<div style="width:700;padding-top:5px; float:left;">
- <h2>Currently Installed Plugins</h2>
+<div class="pull-left" style="width:700;padding-top:5px;">
+ <h2><?php echo __('Currently Installed Plugins'); ?></h2>
 </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
- <b><a href="plugins.php?a=add" class="Icon form-add">Add New Plugin</a></b></div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
+ <b><a href="plugins.php?a=add" class="Icon form-add"><?php
+ echo __('Add New Plugin'); ?></a></b></div>
 <div class="clear"></div>
 
 <?php
@@ -10,7 +11,7 @@ $page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1;
 $count = count($ost->plugins->allInstalled());
 $pageNav = new Pagenate($count, $page, PAGE_LIMIT);
 $pageNav->setURL('forms.php');
-$showing=$pageNav->showing().' forms';
+$showing=$pageNav->showing().' '._N('plugin', 'plugins', $count);
 ?>
 
 <form action="plugins.php" method="POST" name="forms">
@@ -21,9 +22,9 @@ $showing=$pageNav->showing().' forms';
     <thead>
         <tr>
             <th width="7">&nbsp;</th>
-            <th>Plugin Name</th>
-            <th>Status</td>
-            <th>Date Installed</th>
+            <th><?php echo __('Plugin Name'); ?></th>
+            <th><?php echo __('Status'); ?></td>
+            <th><?php echo __('Date Installed'); ?></th>
         </tr>
     </thead>
     <tbody>
@@ -46,12 +47,13 @@ foreach ($ost->plugins->allInstalled() as $p) {
      <tr>
         <td colspan="4">
             <?php if($count){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select'); ?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All'); ?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None'); ?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle'); ?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No plugins installed yet &mdash; <a href="?a=add">add one</a>!';
+                echo sprintf(__('No plugins installed yet &mdash; %s add one %s!'),
+                    '<a href="?a=add">','</a>');
             } ?>
         </td>
      </tr>
@@ -59,37 +61,44 @@ foreach ($ost->plugins->allInstalled() as $p) {
 </table>
 <?php
 if ($count) //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="delete" value="Delete">
-    <input class="button" type="submit" name="enable" value="Enable">
-    <input class="button" type="submit" name="disable" value="Disable">
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete'); ?>">
+    <input class="button" type="submit" name="enable" value="<?php echo __('Enable'); ?>">
+    <input class="button" type="submit" name="disable" value="<?php echo __('Disable'); ?>">
 </p>
 </form>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm'); ?></h3>
     <a class="close" href="">&times;</a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected plugins?</strong></font>
-        <br><br>Deleted forms CANNOT be recovered.
+        <font color="red"><strong><?php echo sprintf(
+        __('Are you sure you want to DELETE %s?'),
+        _N('selected plugin', 'selected plugins', 2)); ?></strong></font>
+        <br><br><?php echo __(
+        'Configuration for deleted plugins CANNOT be recovered.'); ?>
     </p>
     <p class="confirm-action" style="display:none;" id="enable-confirm">
-        <font color="green"><strong>Are you ready to enable selected plugins?</strong></font>
+        <font color="green"><?php echo sprintf(
+        __('Are you sure want to <b>enable</b> %s?'),
+        _N('selected plugin', 'selected plugins', 2)); ?></font>
     </p>
     <p class="confirm-action" style="display:none;" id="disable-confirm">
-        <font color="red"><strong>Are you sure you want to disable selected plugins?</strong></font>
+        <font color="red"><?php echo sprintf(
+        __('Are you sure want to <b>disable</b> %s?'),
+        _N('selected plugin', 'selected plugins', 2)); ?></font>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.'); ?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel'); ?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!'); ?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/profile.inc.php b/include/staff/profile.inc.php
index 0c34376116ea8b5086a63c756d3a36a4feecd7f9..a5daad3c52557183cc6d24a88fdf328cdb753b14 100644
--- a/include/staff/profile.inc.php
+++ b/include/staff/profile.inc.php
@@ -10,27 +10,27 @@ $info['id']=$staff->getId();
  <?php csrf_token(); ?>
  <input type="hidden" name="do" value="update">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2>My Account Profile</h2>
+ <h2><?php echo __('My Account Profile');?></h2>
  <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
         <tr>
             <th colspan="2">
-                <h4>Account Information</h4>
-                <em>Contact information.</em>
+                <h4><?php echo __('Account Information');?></h4>
+                <em><?php echo __('Contact information');?></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <td width="180" class="required">
-                Username:
+                <?php echo __('Username');?>:
             </td>
             <td><b><?php echo $staff->getUserName(); ?></b>&nbsp;<i class="help-tip icon-question-sign" href="#username"></i></td>
         </tr>
 
         <tr>
             <td width="180" class="required">
-                First Name:
+                <?php echo __('First Name');?>:
             </td>
             <td>
                 <input type="text" size="34" name="firstname" value="<?php echo $info['firstname']; ?>">
@@ -39,7 +39,7 @@ $info['id']=$staff->getId();
         </tr>
         <tr>
             <td width="180" class="required">
-                Last Name:
+                <?php echo __('Last Name');?>:
             </td>
             <td>
                 <input type="text" size="34" name="lastname" value="<?php echo $info['lastname']; ?>">
@@ -48,7 +48,7 @@ $info['id']=$staff->getId();
         </tr>
         <tr>
             <td width="180" class="required">
-                Email Address:
+                <?php echo __('Email Address');?>:
             </td>
             <td>
                 <input type="text" size="34" name="email" value="<?php echo $info['email']; ?>">
@@ -57,7 +57,7 @@ $info['id']=$staff->getId();
         </tr>
         <tr>
             <td width="180">
-                Phone Number:
+                <?php echo __('Phone Number');?>:
             </td>
             <td>
                 <input type="text" size="22" name="phone" value="<?php echo $info['phone']; ?>">
@@ -68,7 +68,7 @@ $info['id']=$staff->getId();
         </tr>
         <tr>
             <td width="180">
-                Mobile Number:
+                <?php echo __('Mobile Number');?>:
             </td>
             <td>
                 <input type="text" size="22" name="mobile" value="<?php echo $info['mobile']; ?>">
@@ -77,16 +77,16 @@ $info['id']=$staff->getId();
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Preferences</strong>: Profile preferences and settings.</em>
+                <em><strong><?php echo __('Preferences');?></strong>: <?php echo __('Profile preferences and settings.');?></em>
             </th>
         </tr>
         <tr>
             <td width="180" class="required">
-                Time Zone:
+                <?php echo __('Time Zone');?>:
             </td>
             <td>
                 <select name="timezone_id" id="timezone_id">
-                    <option value="0">&mdash; Select Time Zone &mdash;</option>
+                    <option value="0">&mdash; <?php echo __('Select Time Zone');?> &mdash;</option>
                     <?php
                     $sql='SELECT id, offset,timezone FROM '.TIMEZONE_TABLE.' ORDER BY id';
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -102,17 +102,17 @@ $info['id']=$staff->getId();
         </tr>
         <tr>
             <td width="180">
-                Preferred Language:
+                <?php echo __('Preferred Language'); ?>:
             </td>
             <td>
         <?php
         $langs = Internationalization::availableLanguages(); ?>
                 <select name="lang">
-                    <option value="">&mdash; Use Browser Preference &mdash;</option>
+                    <option value="">&mdash; <?php echo __('Use Browser Preference'); ?> &mdash;</option>
 <?php foreach($langs as $l) {
     $selected = ($info['lang'] == $l['code']) ? 'selected="selected"' : ''; ?>
                     <option value="<?php echo $l['code']; ?>" <?php echo $selected;
-                        ?>><?php echo $l['desc']; ?></option>
+                        ?>><?php echo Internationalization::getLanguageDescription($l['code']); ?></option>
 <?php } ?>
                 </select>
                 <span class="error">&nbsp;<?php echo $errors['lang']; ?></span>
@@ -120,95 +120,100 @@ $info['id']=$staff->getId();
         </tr>
         <tr>
             <td width="180">
-               Daylight Saving:
+               <?php echo __('Daylight Saving');?>:
             </td>
             <td>
                 <input type="checkbox" name="daylight_saving" value="1" <?php echo $info['daylight_saving']?'checked="checked"':''; ?>>
-                Observe daylight saving
-                <em>(Current Time: <strong><?php echo Format::date($cfg->getDateTimeFormat(),Misc::gmtime(),$info['tz_offset'],$info['daylight_saving']); ?></strong>)</em>
+                <?php echo __('Observe daylight saving');?>
+                <em>(<?php echo __('Current Time');?>: <strong><?php echo Format::date($cfg->getDateTimeFormat(),Misc::gmtime(),$info['tz_offset'],$info['daylight_saving']); ?></strong>)</em>
             </td>
         </tr>
         <tr>
-            <td width="180">Maximum Page size:</td>
+            <td width="180"><?php echo __('Maximum Page size');?>:</td>
             <td>
                 <select name="max_page_size">
-                    <option value="0">&mdash; system default &mdash;</option>
+                    <option value="0">&mdash; <?php echo __('system default');?> &mdash;</option>
                     <?php
                     $pagelimit=$info['max_page_size']?$info['max_page_size']:$cfg->getPageSize();
                     for ($i = 5; $i <= 50; $i += 5) {
                         $sel=($pagelimit==$i)?'selected="selected"':'';
-                         echo sprintf('<option value="%d" %s>show %s records</option>',$i,$sel,$i);
+                         echo sprintf('<option value="%d" %s>'.__('show %s records').'</option>',$i,$sel,$i);
                     } ?>
-                </select> per page.
+                </select> <?php echo __('per page.');?>
             </td>
         </tr>
         <tr>
-            <td width="180">Auto Refresh Rate:</td>
+            <td width="180"><?php echo __('Auto Refresh Rate');?>:</td>
             <td>
                 <select name="auto_refresh_rate">
-                  <option value="0">&mdash; disable &mdash;</option>
+                  <option value="0">&mdash; <?php echo __('disable');?> &mdash;</option>
                   <?php
                   $y=1;
                    for($i=1; $i <=30; $i+=$y) {
                      $sel=($info['auto_refresh_rate']==$i)?'selected="selected"':'';
-                     echo sprintf('<option value="%d" %s>Every %s %s</option>',$i,$sel,$i,($i>1?'mins':'min'));
+                     echo sprintf('<option value="%1$d" %2$s>'
+                        .sprintf(
+                            _N('Every minute', 'Every %d minutes', $i), $i)
+                         .'</option>',$i,$sel);
                      if($i>9)
                         $y=2;
                    } ?>
                 </select>
-                <em>(Tickets page refresh rate in minutes.)</em>
+                <em><?php echo __('(Tickets page refresh rate in minutes.)');?></em>
             </td>
         </tr>
         <tr>
-            <td width="180">Default Signature:</td>
+            <td width="180"><?php echo __('Default Signature');?>:</td>
             <td>
                 <select name="default_signature_type">
-                  <option value="none" selected="selected">&mdash; None &mdash;</option>
+                  <option value="none" selected="selected">&mdash; <?php echo __('None');?> &mdash;</option>
                   <?php
-                  $options=array('mine'=>'My Signature','dept'=>'Dept. Signature (if set)');
+                   $options=array('mine'=>__('My Signature'),'dept'=>sprintf(__('Department Signature (%s)'),
+                       __('if set' /* This is used in 'Department Signature (>if set<)' */)));
                   foreach($options as $k=>$v) {
                       echo sprintf('<option value="%s" %s>%s</option>',
                                 $k,($info['default_signature_type']==$k)?'selected="selected"':'',$v);
                   }
                   ?>
                 </select>
-                <em>(You can change selection on ticket page)</em>
+                <em><?php echo __('(This can be selectected when replying to a ticket)');?></em>
                 &nbsp;<span class="error">&nbsp;<?php echo $errors['default_signature_type']; ?></span>
             </td>
         </tr>
         <tr>
-            <td width="180">Default Paper Size:</td>
+            <td width="180"><?php echo __('Default Paper Size');?>:</td>
             <td>
                 <select name="default_paper_size">
-                  <option value="none" selected="selected">&mdash; None &mdash;</option>
+                  <option value="none" selected="selected">&mdash; <?php echo __('None');?> &mdash;</option>
                   <?php
-                  $options=array('Letter', 'Legal', 'A4', 'A3');
-                  foreach($options as $v) {
+
+                  foreach(Export::$paper_sizes as $v) {
                       echo sprintf('<option value="%s" %s>%s</option>',
-                                $v,($info['default_paper_size']==$v)?'selected="selected"':'',$v);
+                                $v,($info['default_paper_size']==$v)?'selected="selected"':'',__($v));
                   }
                   ?>
                 </select>
-                <em>Paper size used when printing tickets to PDF</em>
+                <em><?php echo __('Paper size used when printing tickets to PDF');?></em>
                 &nbsp;<span class="error">&nbsp;<?php echo $errors['default_paper_size']; ?></span>
             </td>
         </tr>
         <tr>
-            <td>Show Assigned Tickets:</td>
+            <td><?php echo __('Show Assigned Tickets');?>:</td>
             <td>
                 <input type="checkbox" name="show_assigned_tickets" <?php echo $info['show_assigned_tickets']?'checked="checked"':''; ?>>
-                <em>Show assigned tickets on open queue.&nbsp;<i class="help-tip icon-question-sign" href="#show_assigned_tickets"></i></em>
+                <em><?php echo __('Show assigned tickets on open queue.');?></em>
+                &nbsp;<i class="help-tip icon-question-sign" href="#show_assigned_tickets"></i></em>
             </td>
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Password</strong>: To reset your password, provide your current password and a new password below.&nbsp;<span class="error">&nbsp;<?php echo $errors['passwd']; ?></span></em>
+                <em><strong><?php echo __('Password');?></strong>: <?php echo __('To reset your password, provide your current password and a new password below.');?>&nbsp;<span class="error">&nbsp;<?php echo $errors['passwd']; ?></span></em>
             </th>
         </tr>
         <?php if (!isset($_SESSION['_staff']['reset-token'])) { ?>
         <tr>
             <td width="180">
-                Current Password:
+                <?php echo __('Current Password');?>:
             </td>
             <td>
                 <input type="password" size="18" name="cpasswd" value="<?php echo $info['cpasswd']; ?>">
@@ -218,7 +223,7 @@ $info['id']=$staff->getId();
         <?php } ?>
         <tr>
             <td width="180">
-                New Password:
+                <?php echo __('New Password');?>:
             </td>
             <td>
                 <input type="password" size="18" name="passwd1" value="<?php echo $info['passwd1']; ?>">
@@ -227,7 +232,7 @@ $info['id']=$staff->getId();
         </tr>
         <tr>
             <td width="180">
-                Confirm New Password:
+                <?php echo __('Confirm New Password');?>:
             </td>
             <td>
                 <input type="password" size="18" name="passwd2" value="<?php echo $info['passwd2']; ?>">
@@ -236,7 +241,7 @@ $info['id']=$staff->getId();
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Signature</strong>: Optional signature used on outgoing emails.
+                <em><strong><?php echo __('Signature');?></strong>: <?php echo __('Optional signature used on outgoing emails.');?>
                 &nbsp;<span class="error">&nbsp;<?php echo $errors['signature']; ?></span>&nbsp;<i class="help-tip icon-question-sign" href="#signature"></i></em>
             </th>
         </tr>
@@ -244,14 +249,14 @@ $info['id']=$staff->getId();
             <td colspan=2>
                 <textarea class="richtext no-bar" name="signature" cols="21"
                     rows="5" style="width: 60%;"><?php echo $info['signature']; ?></textarea>
-                <br><em>Signature is made available as a choice, on ticket reply.</em>
+                <br><em><?php __('Signature is made available as a choice, on ticket reply.');?></em>
             </td>
         </tr>
     </tbody>
 </table>
-<p style="padding-left:200px;">
-    <input type="submit" name="submit" value="Save Changes">
-    <input type="reset"  name="reset"  value="Reset Changes">
-    <input type="button" name="cancel" value="Cancel Changes" onclick='window.location.href="index.php"'>
+<p style="text-align:center;">
+    <input type="submit" name="submit" value="<?php echo __('Save Changes');?>">
+    <input type="reset"  name="reset"  value="<?php echo __('Reset Changes');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel Changes');?>" onclick='window.location.href="index.php"'>
 </p>
 </form>
diff --git a/include/staff/pwreset.login.php b/include/staff/pwreset.login.php
index 8a7455ad8b28d5283b0f3ee6cd3f303f3262af6a..29670e28f6a1deed29d69fd37887c9f326dd48f7 100644
--- a/include/staff/pwreset.login.php
+++ b/include/staff/pwreset.login.php
@@ -5,7 +5,7 @@ $info = ($_POST)?Format::htmlchars($_POST):array();
 ?>
 
 <div id="loginBox">
-    <h1 id="logo"><a href="index.php">osTicket Staff Password Reset</a></h1>
+    <h1 id="logo"><a href="index.php">osTicket <?php echo __('Agent Password Reset'); ?></a></h1>
     <h3><?php echo Format::htmlchars($msg); ?></h3>
 
     <form action="pwreset.php" method="post">
@@ -14,7 +14,7 @@ $info = ($_POST)?Format::htmlchars($_POST):array();
         <input type="hidden" name="token" value="<?php echo Format::htmlchars($_REQUEST['token']); ?>"/>
         <fieldset>
             <input type="text" name="userid" id="name" value="<?php echo
-                $info['userid']; ?>" placeholder="username or email"
+                $info['userid']; ?>" placeholder="<?php echo __('Email or Username'); ?>"
                 autocorrect="off" autocapitalize="off"/>
         </fieldset>
         <input class="submit" type="submit" name="submit" value="Login"/>
diff --git a/include/staff/pwreset.php b/include/staff/pwreset.php
index 6aadeb2fcf10dbb308ed7ec3f548f4afd6c94ee8..22157a36cb563a50d9421ec88eef736dbb14dd76 100644
--- a/include/staff/pwreset.php
+++ b/include/staff/pwreset.php
@@ -5,17 +5,17 @@ $info = ($_POST && $errors)?Format::htmlchars($_POST):array();
 ?>
 
 <div id="loginBox">
-    <h1 id="logo"><a href="index.php">osTicket Staff Password Reset</a></h1>
+    <h1 id="logo"><a href="index.php">osTicket <?php echo __('Agent Password Reset'); ?></a></h1>
     <h3><?php echo Format::htmlchars($msg); ?></h3>
     <form action="pwreset.php" method="post">
         <?php csrf_token(); ?>
         <input type="hidden" name="do" value="sendmail">
         <fieldset>
             <input type="text" name="userid" id="name" value="<?php echo
-                $info['userid']; ?>" placeholder="username" autocorrect="off"
+            $info['userid']; ?>" placeholder="<?php echo __('Email or Username'); ?>" autocorrect="off"
                 autocapitalize="off">
         </fieldset>
-        <input class="submit" type="submit" name="submit" value="Send Email"/>
+        <input class="submit" type="submit" name="submit" value="<?php echo __('Send Email'); ?>"/>
     </form>
 
 </div>
diff --git a/include/staff/pwreset.sent.php b/include/staff/pwreset.sent.php
index 832b78ef57470c4045ed615c152dc228eb414e79..2825c0c584872354e48bd58010fdd770bb268563 100644
--- a/include/staff/pwreset.sent.php
+++ b/include/staff/pwreset.sent.php
@@ -5,11 +5,11 @@ $info = ($_POST && $errors)?Format::htmlchars($_POST):array();
 ?>
 
 <div id="loginBox">
-    <h1 id="logo"><a href="index.php">osTicket Staff Password Reset</a></h1>
-    <h3>A confirmation email has been sent</h3>
-    <h3 style="color:black;"><em>
-    A password reset email was sent to the email on file for your account.
-    Follow the link in the email to reset your password.
+    <h1 id="logo"><a href="index.php">osTicket <?php echo __('Agent Password Reset'); ?></a></h1>
+    <h3><?php echo __('A confirmation email has been sent'); ?></h3>
+    <h3 style="color:black;"><em><?php echo __(
+    'A password reset email was sent to the email on file for your account.  Follow the link in the email to reset your password.'
+    ); ?>
     </em></h3>
 
     <form action="index.php" method="get">
diff --git a/include/staff/settings-access.inc.php b/include/staff/settings-access.inc.php
index e045a45600f52fa7abbcfc77f3b9b9baaab3b319..fa6fc607e77d84f5383723785966311a5a8c3005 100644
--- a/include/staff/settings-access.inc.php
+++ b/include/staff/settings-access.inc.php
@@ -2,7 +2,7 @@
 if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config) die('Access Denied');
 
 ?>
-<h2>Access Control Settings</h2>
+<h2><?php echo __('Access Control Settings'); ?></h2>
 <form action="settings.php?t=access" method="post" id="save">
 <?php csrf_token(); ?>
 <input type="hidden" name="t" value="access" >
@@ -10,24 +10,25 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
     <thead>
         <tr>
             <th colspan="2">
-                <h4>Configure Access to this Help Desk</h4>
+                <h4><?php echo __('Configure Access to this Help Desk'); ?></h4>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <th colspan="2">
-                <em><b>Staff Authentication Settings</b></em>
+                <em><b><?php echo __('Agent Authentication Settings'); ?></b></em>
             </th>
         </tr>
-        <tr><td>Password Expiration Policy:</th>
+        <tr><td><?php echo __('Password Expiration Policy'); ?>:</th>
             <td>
                 <select name="passwd_reset_period">
-                   <option value="0"> &mdash; No expiration &mdash;</option>
+                   <option value="0"> &mdash; <?php echo __('No expiration'); ?> &mdash;</option>
                   <?php
                     for ($i = 1; $i <= 12; $i++) {
-                        echo sprintf('<option value="%d" %s>%s%s</option>',
-                                $i,(($config['passwd_reset_period']==$i)?'selected="selected"':''), $i>1?"Every $i ":'', $i>1?' Months':'Monthly');
+                        echo sprintf('<option value="%d" %s>%s</option>',
+                                $i,(($config['passwd_reset_period']==$i)?'selected="selected"':''),
+                                sprintf(_N('Monthly', 'Every %d months', $i), $i));
                     }
                     ?>
                 </select>
@@ -35,21 +36,22 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
                 <i class="help-tip icon-question-sign" href="#password_expiration_policy"></i>
             </td>
         </tr>
-        <tr><td>Allow Password Resets:</th>
+        <tr><td><?php echo __('Allow Password Resets'); ?>:</th>
             <td>
               <input type="checkbox" name="allow_pw_reset" <?php echo $config['allow_pw_reset']?'checked="checked"':''; ?>>
               &nbsp;<i class="help-tip icon-question-sign" href="#allow_password_resets"></i>
             </td>
         </tr>
-        <tr><td>Reset Token Expiration:</th>
+        <tr><td><?php echo __('Reset Token Expiration'); ?>:</th>
             <td>
               <input type="text" name="pw_reset_window" size="6" value="<?php
                     echo $config['pw_reset_window']; ?>">
-                <em>mins</em>&nbsp;<i class="help-tip icon-question-sign" href="#reset_token_expiration"></i>
-                &nbsp;<font class="error">&nbsp;<?php echo $errors['pw_reset_window']; ?></font>
+                    <em><?php echo __('minutes'); ?></em>
+                    <i class="help-tip icon-question-sign" href="#reset_token_expiration"></i>
+                &nbsp;<font class="error"><?php echo $errors['pw_reset_window']; ?></font>
             </td>
         </tr>
-        <tr><td>Staff Excessive Logins:</td>
+        <tr><td><?php echo __('Agent Excessive Logins'); ?>:</td>
             <td>
                 <select name="staff_max_logins">
                   <?php
@@ -57,23 +59,25 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
                         echo sprintf('<option value="%d" %s>%d</option>', $i,(($config['staff_max_logins']==$i)?'selected="selected"':''), $i);
                     }
                     ?>
-                </select> failed login attempt(s) allowed before a
+                </select> <?php echo __(
+                'failed login attempt(s) allowed before a lock-out is enforced'); ?>
+                <br/>
                 <select name="staff_login_timeout">
                   <?php
                     for ($i = 1; $i <= 10; $i++) {
                         echo sprintf('<option value="%d" %s>%d</option>', $i,(($config['staff_login_timeout']==$i)?'selected="selected"':''), $i);
                     }
                     ?>
-                </select> minute lock-out is enforced.
+                </select> <?php echo __('minutes locked out'); ?>
             </td>
         </tr>
-        <tr><td>Staff Session Timeout:</td>
+        <tr><td><?php echo __('Agent Session Timeout'); ?>:</td>
             <td>
               <input type="text" name="staff_session_timeout" size=6 value="<?php echo $config['staff_session_timeout']; ?>">
-                mins <em>( 0 to disable)</em>. <i class="help-tip icon-question-sign" href="#staff_session_timeout"></i>
+                <?php echo __('minutes'); ?> <em><?php echo __('(0 to disable)'); ?></em>. <i class="help-tip icon-question-sign" href="#staff_session_timeout"></i>
             </td>
         </tr>
-        <tr><td>Bind Staff Session to IP:</td>
+        <tr><td><?php echo __('Bind Agent Session to IP'); ?>:</td>
             <td>
               <input type="checkbox" name="staff_ip_binding" <?php echo $config['staff_ip_binding']?'checked="checked"':''; ?>>
               <i class="help-tip icon-question-sign" href="#bind_staff_session_to_ip"></i>
@@ -81,22 +85,22 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
         </tr>
         <tr>
             <th colspan="2">
-                <em><b>End User Authentication Settings</b></em>
+                <em><b><?php echo __('End User Authentication Settings'); ?></b></em>
             </th>
         </tr>
-        <tr><td>Registration Required:</td>
+        <tr><td><?php echo __('Registration Required'); ?>:</td>
             <td><input type="checkbox" name="clients_only" <?php
                 if ($config['clients_only'])
-                    echo 'checked="checked"'; ?>/>
-                Require registration and login to create tickets
+                    echo 'checked="checked"'; ?>/> <?php echo __(
+                    'Require registration and login to create tickets'); ?>
             <i class="help-tip icon-question-sign" href="#registration_method"></i>
             </td>
-        <tr><td>Registration Method:</td>
+        <tr><td><?php echo __('Registration Method'); ?>:</td>
             <td><select name="client_registration">
 <?php foreach (array(
-    'disabled' => 'Disabled — All users are guests',
-    'public' => 'Public — Anyone can register',
-    'closed' => 'Private — Only staff can register users',)
+    'disabled' => __('Disabled — All users are guests'),
+    'public' => __('Public — Anyone can register'),
+    'closed' => __('Private — Only agents can register users'),)
     as $key=>$val) { ?>
         <option value="<?php echo $key; ?>" <?php
         if ($config['client_registration'] == $key)
@@ -107,7 +111,7 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
             <i class="help-tip icon-question-sign" href="#registration_method"></i>
             </td>
         </tr>
-        <tr><td>User Excessive Logins:</td>
+        <tr><td><?php echo __('User Excessive Logins'); ?>:</td>
             <td>
                 <select name="client_max_logins">
                   <?php
@@ -116,27 +120,29 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
                     }
 
                     ?>
-                </select> failed login attempt(s) allowed before a
+                </select> <?php echo __(
+                'failed login attempt(s) allowed before a lock-out is enforced'); ?>
+                <br/>
                 <select name="client_login_timeout">
                   <?php
                     for ($i = 1; $i <= 10; $i++) {
                         echo sprintf('<option value="%d" %s>%d</option>', $i,(($config['client_login_timeout']==$i)?'selected="selected"':''), $i);
                     }
                     ?>
-                </select> minute lock-out is enforced.
+                </select> <?php echo __('minutes locked out'); ?>
             </td>
         </tr>
-        <tr><td>User Session Timeout:</td>
+        <tr><td><?php echo __('User Session Timeout'); ?>:</td>
             <td>
               <input type="text" name="client_session_timeout" size=6 value="<?php echo $config['client_session_timeout']; ?>">
               <i class="help-tip icon-question-sign" href="#client_session_timeout"></i>
             </td>
         </tr>
-        <tr><td>Client Quick Access:</td>
+        <tr><td><?php echo __('Client Quick Access'); ?>:</td>
             <td><input type="checkbox" name="client_verify_email" <?php
                 if ($config['client_verify_email'])
-                    echo 'checked="checked"'; ?>/>
-                Require email verification on "Check Ticket Status" page
+                    echo 'checked="checked"'; ?>/> <?php echo __(
+                'Require email verification on "Check Ticket Status" page'); ?>
             <i class="help-tip icon-question-sign" href="#client_verify_email"></i>
             </td>
         </tr>
@@ -144,7 +150,7 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
     <thead>
         <tr>
             <th colspan="2">
-                <h4>Authentication and Registration Templates</h4>
+                <h4><?php echo __('Authentication and Registration Templates'); ?></h4>
             </th>
         </tr>
     </thead>
@@ -170,41 +176,43 @@ $manage_content = function($title, $content) use ($contents) {
     echo Format::htmlchars($title); ?></a><br/>
         <span class="faded" style="display:inline-block;width:90%"><?php
         echo Format::display($notes); ?>
-    <em>(Last Updated <?php echo Format::db_datetime($upd); ?>)</em></span></td></tr><?php
+    <em>(<?php echo sprintf(__('Last Updated %s'), Format::db_datetime($upd));
+        ?>)</em></span></td></tr><?php
 }; ?>
         <tr>
             <th colspan="2">
-                <em><b>Authentication and Registration Templates</b></em>
+                <em><b><?php echo __(
+                'Authentication and Registration Templates'); ?></b></em>
             </th>
         </tr>
-        <?php $manage_content('Staff Members', 'pwreset-staff'); ?>
-        <?php $manage_content('Clients', 'pwreset-client'); ?>
-        <?php $manage_content('Guess Ticket Access', 'access-link'); ?>
+        <?php $manage_content(__('Agents'), 'pwreset-staff'); ?>
+        <?php $manage_content(__('Clients'), 'pwreset-client'); ?>
+        <?php $manage_content(__('Guest Ticket Access'), 'access-link'); ?>
         <tr>
             <th colspan="2">
-                <em><b>Sign-In Pages</b></em>
+                <em><b><?php echo __('Sign In Pages'); ?></b></em>
             </th>
         </tr>
-        <?php $manage_content('Staff Login Banner', 'banner-staff'); ?>
-        <?php $manage_content('Client Sign-In Page', 'banner-client'); ?>
+        <?php $manage_content(__('Agent Login Banner'), 'banner-staff'); ?>
+        <?php $manage_content(__('Client Sign-In Page'), 'banner-client'); ?>
         <tr>
             <th colspan="2">
-                <em><b>User Account Registration</b></em>
+                <em><b><?php echo __('User Account Registration'); ?></b></em>
             </th>
         </tr>
-        <?php $manage_content('Please Confirm Email Address Page', 'registration-confirm'); ?>
-        <?php $manage_content('Confirmation Email', 'registration-client'); ?>
-        <?php $manage_content('Account Confirmed Page', 'registration-thanks'); ?>
+        <?php $manage_content(__('Please Confirm Email Address Page'), 'registration-confirm'); ?>
+        <?php $manage_content(__('Confirmation Email'), 'registration-client'); ?>
+        <?php $manage_content(__('Account Confirmed Page'), 'registration-thanks'); ?>
         <tr>
             <th colspan="2">
-                <em><b>Staff Account Registration</b></em>
+                <em><b><?php echo __('Agent Account Registration'); ?></b></em>
             </th>
         </tr>
-        <?php $manage_content('Staff Welcome Email', 'registration-staff'); ?>
+        <?php $manage_content(__('Agent Welcome Email'), 'registration-staff'); ?>
 </tbody>
 </table>
 <p style="text-align:center">
-    <input class="button" type="submit" name="submit" value="Save Changes">
-    <input class="button" type="reset" name="reset" value="Reset Changes">
+    <input class="button" type="submit" name="submit" value="<?php echo __('Save Changes'); ?>">
+    <input class="button" type="reset" name="reset" value="<?php echo __('Reset Changes'); ?>">
 </p>
 </form>
diff --git a/include/staff/settings-alerts.inc.php b/include/staff/settings-alerts.inc.php
index 992845d4d4808a17b5e007bbc3a6cfdaec82d747..14b3fee30ade06c2b8d190b83e276cb295dd2351 100644
--- a/include/staff/settings-alerts.inc.php
+++ b/include/staff/settings-alerts.inc.php
@@ -1,4 +1,4 @@
-<h2>Alerts and Notices
+<h2><?php echo __('Alerts and Notices'); ?>
     <i class="help-tip icon-question-sign" href="#page_title"></i></h2>
 <form action="settings.php?t=alerts" method="post" id="save">
 <?php csrf_token(); ?>
@@ -7,215 +7,234 @@
     <thead>
         <tr>
             <th>
-                <h4>Alerts and Notices sent to staff on ticket "events"</h4>
+                <h4><?php echo __('Alerts and Notices sent to agents on ticket "events"'); ?></h4>
             </th>
         </tr>
     </thead>
     <tbody>
-        <tr><th><em><b>New Ticket Alert</b>:
+        <tr><th><em><b><?php echo __('New Ticket Alert'); ?></b>:
             <i class="help-tip icon-question-sign" href="#ticket_alert"></i>
             </em></th></tr>
         <tr>
-            <td><em><b>Status:</b></em> &nbsp;
+            <td><em><b><?php echo __('Status'); ?>:</b></em> &nbsp;
                 <input type="radio" name="ticket_alert_active"  value="1"
                 <?php echo $config['ticket_alert_active']?'checked':''; ?>
-                /> Enable
-                <input type="radio" name="ticket_alert_active"  value="0"   <?php echo !$config['ticket_alert_active']?'checked':''; ?> /> Disable
+                /> <?php echo __('Enable'); ?>
+                <input type="radio" name="ticket_alert_active"  value="0"   <?php echo !$config['ticket_alert_active']?'checked':''; ?> />
+                 <?php echo __('Disable'); ?>
                 &nbsp;&nbsp;<font class="error">&nbsp;<?php echo $errors['ticket_alert_active']; ?></font></em>
              </td>
         </tr>
         <tr>
             <td>
-                <input type="checkbox" name="ticket_alert_admin" <?php echo $config['ticket_alert_admin']?'checked':''; ?>> Admin Email <em>(<?php echo $cfg->getAdminEmail(); ?>)</em>
+                <input type="checkbox" name="ticket_alert_admin" <?php echo $config['ticket_alert_admin']?'checked':''; ?>>
+                <?php echo __('Admin Email'); ?> <em>(<?php echo $cfg->getAdminEmail(); ?>)</em>
             </td>
         </tr>
         <tr>
             <td>
-                <input type="checkbox" name="ticket_alert_dept_manager" <?php echo $config['ticket_alert_dept_manager']?'checked':''; ?>> Department Manager
+                <input type="checkbox" name="ticket_alert_dept_manager" <?php echo $config['ticket_alert_dept_manager']?'checked':''; ?>>
+                <?php echo __('Department Manager'); ?>
             </td>
         </tr>
         <tr>
             <td>
-                <input type="checkbox" name="ticket_alert_dept_members" <?php echo $config['ticket_alert_dept_members']?'checked':''; ?>> Department Members
+                <input type="checkbox" name="ticket_alert_dept_members" <?php echo $config['ticket_alert_dept_members']?'checked':''; ?>>
+                <?php echo __('Department Members'); ?>
             </td>
         </tr>
         <tr>
             <td>
-                <input type="checkbox" name="ticket_alert_acct_manager" <?php echo $config['ticket_alert_acct_manager']?'checked':''; ?>> Organization Account Manager
+                <input type="checkbox" name="ticket_alert_acct_manager" <?php echo $config['ticket_alert_acct_manager']?'checked':''; ?>>
+                <?php echo __('Organization Account Manager'); ?>
             </td>
         </tr>
-        <tr><th><em><b>New Message Alert</b>:
+        <tr><th><em><b><?php echo __('New Message Alert'); ?></b>:
             <i class="help-tip icon-question-sign" href="#message_alert"></i>
             </em></th></tr>
         <tr>
-            <td><em><b>Status:</b></em> &nbsp;
+            <td><em><b><?php echo __('Status'); ?>:</b></em> &nbsp;
               <input type="radio" name="message_alert_active"  value="1"
               <?php echo $config['message_alert_active']?'checked':''; ?>
-              /> Enable
+              /> <?php echo __('Enable'); ?>
               &nbsp;&nbsp;
-              <input type="radio" name="message_alert_active"  value="0"   <?php echo !$config['message_alert_active']?'checked':''; ?> /> Disable
+              <input type="radio" name="message_alert_active"  value="0"   <?php echo !$config['message_alert_active']?'checked':''; ?> />
+              <?php echo __('Disable'); ?>
             </td>
         </tr>
         <tr>
             <td>
-              <input type="checkbox" name="message_alert_laststaff" <?php echo $config['message_alert_laststaff']?'checked':''; ?>> Last Respondent
+              <input type="checkbox" name="message_alert_laststaff" <?php echo $config['message_alert_laststaff']?'checked':''; ?>>
+                <?php echo __('Last Respondent'); ?>
             </td>
         </tr>
         <tr>
             <td>
               <input type="checkbox" name="message_alert_assigned" <?php
               echo $config['message_alert_assigned']?'checked':''; ?>>
-              Assigned Staff
+              <?php echo __('Assigned Agent / Team'); ?>
             </td>
         </tr>
         <tr>
             <td>
               <input type="checkbox" name="message_alert_dept_manager" <?php
               echo $config['message_alert_dept_manager']?'checked':''; ?>>
-              Department Manager
+              <?php echo __('Department Manager'); ?>
             </td>
         </tr>
         <tr>
             <td>
-                <input type="checkbox" name="message_alert_acct_manager" <?php echo $config['message_alert_acct_manager']?'checked':''; ?>> Organization Account Manager
+                <input type="checkbox" name="message_alert_acct_manager" <?php echo $config['message_alert_acct_manager']?'checked':''; ?>>
+                <?php echo __('Organization Account Manager'); ?>
             </td>
         </tr>
-        <tr><th><em><b>New Internal Note Alert</b>:
+        <tr><th><em><b><?php echo __('New Internal Note Alert'); ?></b>:
             <i class="help-tip icon-question-sign" href="#internal_note_alert"></i>
             </em></th></tr>
         <tr>
-            <td><em><b>Status:</b></em> &nbsp;
-              <input type="radio" name="note_alert_active"  value="1"   <?php echo $config['note_alert_active']?'checked':''; ?> /> Enable
+            <td><em><b><?php echo __('Status'); ?>:</b></em> &nbsp;
+              <input type="radio" name="note_alert_active"  value="1"   <?php echo $config['note_alert_active']?'checked':''; ?> />
+                <?php echo __('Enable'); ?>
               &nbsp;&nbsp;
-              <input type="radio" name="note_alert_active"  value="0"   <?php echo !$config['note_alert_active']?'checked':''; ?> /> Disable
+              <input type="radio" name="note_alert_active"  value="0"   <?php echo !$config['note_alert_active']?'checked':''; ?> />
+                <?php echo __('Disable'); ?>
               &nbsp;&nbsp;&nbsp;<font class="error">&nbsp;<?php echo $errors['note_alert_active']; ?></font>
             </td>
         </tr>
         <tr>
             <td>
               <input type="checkbox" name="note_alert_laststaff" <?php echo
-              $config['note_alert_laststaff']?'checked':''; ?>> Last Respondent
+              $config['note_alert_laststaff']?'checked':''; ?>> <?php echo __('Last Respondent'); ?>
             </td>
         </tr>
         <tr>
             <td>
-              <input type="checkbox" name="note_alert_assigned" <?php echo $config['note_alert_assigned']?'checked':''; ?>> Assigned Staff / Team
+              <input type="checkbox" name="note_alert_assigned" <?php echo $config['note_alert_assigned']?'checked':''; ?>>
+                <?php echo __('Assigned Agent / Team'); ?>
             </td>
         </tr>
         <tr>
             <td>
-              <input type="checkbox" name="note_alert_dept_manager" <?php echo $config['note_alert_dept_manager']?'checked':''; ?>> Department Manager
+              <input type="checkbox" name="note_alert_dept_manager" <?php echo $config['note_alert_dept_manager']?'checked':''; ?>>
+                <?php echo __('Department Manager'); ?>
             </td>
         </tr>
-        <tr><th><em><b>Ticket Assignment Alert</b>:
+        <tr><th><em><b><?php echo __('Ticket Assignment Alert'); ?></b>:
             <i class="help-tip icon-question-sign" href="#assignment_alert"></i>
             </em></th></tr>
         <tr>
-            <td><em><b>Status: </b></em> &nbsp;
+            <td><em><b><?php echo __('Status'); ?>: </b></em> &nbsp;
               <input name="assigned_alert_active" value="1" type="radio"
-                <?php echo $config['assigned_alert_active']?'checked="checked"':''; ?>> Enable
+                <?php echo $config['assigned_alert_active']?'checked="checked"':''; ?>> <?php echo __('Enable'); ?>
               &nbsp;&nbsp;
               <input name="assigned_alert_active" value="0" type="radio"
-                <?php echo !$config['assigned_alert_active']?'checked="checked"':''; ?>> Disable
+                <?php echo !$config['assigned_alert_active']?'checked="checked"':''; ?>> <?php echo __('Disable'); ?>
                &nbsp;&nbsp;&nbsp;<font class="error">&nbsp;<?php echo $errors['assigned_alert_active']; ?></font>
             </td>
         </tr>
         <tr>
             <td>
               <input type="checkbox" name="assigned_alert_staff" <?php echo
-              $config['assigned_alert_staff']?'checked':''; ?>> Assigned Staff
+              $config['assigned_alert_staff']?'checked':''; ?>> <?php echo __('Assigned Agent / Team'); ?>
             </td>
         </tr>
         <tr>
             <td>
               <input type="checkbox"name="assigned_alert_team_lead" <?php
-              echo $config['assigned_alert_team_lead']?'checked':''; ?>> Team Lead
+              echo $config['assigned_alert_team_lead']?'checked':''; ?>> <?php echo __('Team Lead'); ?>
             </td>
         </tr>
         <tr>
             <td>
               <input type="checkbox"name="assigned_alert_team_members" <?php echo $config['assigned_alert_team_members']?'checked':''; ?>>
-                Team Members
+                <?php echo __('Team Members'); ?>
             </td>
         </tr>
-        <tr><th><em><b>Ticket Transfer Alert</b>:
+        <tr><th><em><b><?php echo __('Ticket Transfer Alert'); ?></b>:
             <i class="help-tip icon-question-sign" href="#transfer_alert"></i>
             </em></th></tr>
         <tr>
-            <td><em><b>Status:</b></em> &nbsp;
-              <input type="radio" name="transfer_alert_active"  value="1"   <?php echo $config['transfer_alert_active']?'checked':''; ?> /> Enable
-              <input type="radio" name="transfer_alert_active"  value="0"   <?php echo !$config['transfer_alert_active']?'checked':''; ?> /> Disable
+            <td><em><b><?php echo __('Status'); ?>:</b></em> &nbsp;
+            <input type="radio" name="transfer_alert_active"  value="1"   <?php echo $config['transfer_alert_active']?'checked':''; ?> />
+                <?php echo __('Enable'); ?>
+            <input type="radio" name="transfer_alert_active"  value="0"   <?php echo !$config['transfer_alert_active']?'checked':''; ?> />
+                <?php echo __('Disable'); ?>
               &nbsp;&nbsp;&nbsp;<font class="error">&nbsp;<?php echo $errors['alert_alert_active']; ?></font>
             </td>
         </tr>
         <tr>
             <td>
-              <input type="checkbox" name="transfer_alert_assigned" <?php echo $config['transfer_alert_assigned']?'checked':''; ?>> Assigned Staff / Team
+              <input type="checkbox" name="transfer_alert_assigned" <?php echo $config['transfer_alert_assigned']?'checked':''; ?>>
+                <?php echo __('Assigned Agent / Team'); ?>
             </td>
         </tr>
         <tr>
             <td>
-              <input type="checkbox" name="transfer_alert_dept_manager" <?php echo $config['transfer_alert_dept_manager']?'checked':''; ?>> Department Manager
+              <input type="checkbox" name="transfer_alert_dept_manager" <?php echo $config['transfer_alert_dept_manager']?'checked':''; ?>>
+                <?php echo __('Department Manager'); ?>
             </td>
         </tr>
         <tr>
             <td>
               <input type="checkbox" name="transfer_alert_dept_members" <?php echo $config['transfer_alert_dept_members']?'checked':''; ?>>
-                Department Members
+                <?php echo __('Department Members'); ?>
             </td>
         </tr>
-        <tr><th><em><b>Overdue Ticket Alert</b>:
+        <tr><th><em><b><?php echo __('Overdue Ticket Alert'); ?></b>:
             <i class="help-tip icon-question-sign" href="#overdue_alert"></i>
             </em></th></tr>
         <tr>
-            <td><em><b>Status:</b></em> &nbsp;
+            <td><em><b><?php echo __('Status'); ?>:</b></em> &nbsp;
               <input type="radio" name="overdue_alert_active"  value="1"
-                <?php echo $config['overdue_alert_active']?'checked':''; ?> /> Enable
+                <?php echo $config['overdue_alert_active']?'checked':''; ?> /> <?php echo __('Enable'); ?>
               <input type="radio" name="overdue_alert_active"  value="0"
-                <?php echo !$config['overdue_alert_active']?'checked':''; ?> /> Disable
+                <?php echo !$config['overdue_alert_active']?'checked':''; ?> /> <?php echo __('Disable'); ?>
               &nbsp;&nbsp;<font class="error">&nbsp;<?php echo $errors['overdue_alert_active']; ?></font>
             </td>
         </tr>
         <tr>
             <td>
               <input type="checkbox" name="overdue_alert_assigned" <?php
-                echo $config['overdue_alert_assigned']?'checked':''; ?>> Assigned Staff / Team
+                echo $config['overdue_alert_assigned']?'checked':''; ?>> <?php echo __('Assigned Agent / Team'); ?>
             </td>
         </tr>
         <tr>
             <td>
               <input type="checkbox" name="overdue_alert_dept_manager" <?php
-                echo $config['overdue_alert_dept_manager']?'checked':''; ?>> Department Manager
+                echo $config['overdue_alert_dept_manager']?'checked':''; ?>> <?php echo __('Department Manager'); ?>
             </td>
         </tr>
         <tr>
             <td>
               <input type="checkbox" name="overdue_alert_dept_members" <?php
-                echo $config['overdue_alert_dept_members']?'checked':''; ?>> Department Members
+                echo $config['overdue_alert_dept_members']?'checked':''; ?>> <?php echo __('Department Members'); ?>
             </td>
         </tr>
         <tr><th>
-            <em><b>System Alerts</b>: <i class="help-tip icon-question-sign" href="#system_alerts"></i></em></th></tr>
+            <em><b><?php echo __('System Alerts'); ?></b>: <i class="help-tip icon-question-sign" href="#system_alerts"></i></em></th></tr>
         <tr>
             <td>
-              <input type="checkbox" name="send_sys_errors" checked="checked" disabled="disabled"> System Errors
-              <em>(enabled by default)</em>
+              <input type="checkbox" name="send_sys_errors" checked="checked" disabled="disabled">
+                <?php echo __('System Errors'); ?>
+              <em><?php echo __('(enabled by default)'); ?></em>
             </td>
         </tr>
         <tr>
             <td>
-              <input type="checkbox" name="send_sql_errors" <?php echo $config['send_sql_errors']?'checked':''; ?>> SQL errors
+              <input type="checkbox" name="send_sql_errors" <?php echo $config['send_sql_errors']?'checked':''; ?>>
+                <?php echo __('SQL errors'); ?>
             </td>
         </tr>
         <tr>
             <td>
-              <input type="checkbox" name="send_login_errors" <?php echo $config['send_login_errors']?'checked':''; ?>> Excessive failed login attempts
+              <input type="checkbox" name="send_login_errors" <?php echo $config['send_login_errors']?'checked':''; ?>>
+                <?php echo __('Excessive failed login attempts'); ?>
             </td>
         </tr>
     </tbody>
 </table>
-<p style="padding-left:350px;">
-    <input class="button" type="submit" name="submit" value="Save Changes">
-    <input class="button" type="reset" name="reset" value="Reset Changes">
+<p style="text-align:center;">
+    <input class="button" type="submit" name="submit" value="<?php echo __('Save Changes'); ?>">
+    <input class="button" type="reset" name="reset" value="<?php echo __('Reset Changes'); ?>">
 </p>
 </form>
diff --git a/include/staff/settings-autoresp.inc.php b/include/staff/settings-autoresp.inc.php
index 856a0a7780d50838e977064221e0e70776493c4a..0a59f4b9d215589b6f1cd2ad2540854dc4cb4c7c 100644
--- a/include/staff/settings-autoresp.inc.php
+++ b/include/staff/settings-autoresp.inc.php
@@ -1,4 +1,4 @@
-<h2>Autoresponder Settings</h2>
+<h2><?php echo __('Autoresponder Settings'); ?></h2>
 <form action="settings.php?t=autoresp" method="post" id="save">
 <?php csrf_token(); ?>
 <input type="hidden" name="t" value="autoresp" >
@@ -6,37 +6,37 @@
     <thead>
         <tr>
             <th colspan="2">
-                <h4>Autoresponder Setting</h4>
-                <em>Global setting - can be disabled at department or email level.</em>
+                <h4><?php echo __('Autoresponder Setting'); ?></h4>
+                <em><?php echo __('Global setting - can be disabled at department or email level.'); ?></em>
             </th>
         </tr>
     </thead>
     <tbody>
 
         <tr>
-            <td width="160">New Ticket:</td>
+            <td width="160"><?php echo __('New Ticket'); ?>:</td>
             <td>
                 <input type="checkbox" name="ticket_autoresponder" <?php
 echo $config['ticket_autoresponder'] ? 'checked="checked"' : ''; ?>/>
-                Ticket Owner&nbsp;
+                <?php echo __('Ticket Owner'); ?>&nbsp;
                 <i class="help-tip icon-question-sign" href="#new_ticket"></i>
             </td>
         </tr>
         <tr>
-            <td width="160">New Ticket by Staff:</td>
+            <td width="160"><?php echo __('New Ticket by Agent'); ?>:</td>
             <td>
                 <input type="checkbox" name="ticket_notice_active" <?php
 echo $config['ticket_notice_active'] ? 'checked="checked"' : ''; ?>/>
-                Ticket Owner&nbsp;
+                <?php echo __('Ticket Owner'); ?>&nbsp;
                 <i class="help-tip icon-question-sign" href="#new_ticket_by_staff"></i>
             </td>
         </tr>
         <tr>
-            <td width="160" rowspan="2">New Message:</td>
+            <td width="160" rowspan="2"><?php echo __('New Message'); ?>:</td>
             <td>
                 <input type="checkbox" name="message_autoresponder" <?php
 echo $config['message_autoresponder'] ? 'checked="checked"' : ''; ?>/>
-                Submitter: Send receipt confirmation&nbsp;
+                <?php echo __('Submitter: Send receipt confirmation'); ?>&nbsp;
                 <i class="help-tip icon-question-sign" href="#new_message_for_submitter"></i>
             </td>
         </tr>
@@ -44,24 +44,24 @@ echo $config['message_autoresponder'] ? 'checked="checked"' : ''; ?>/>
             <td>
                 <input type="checkbox" name="message_autoresponder_collabs" <?php
 echo $config['message_autoresponder_collabs'] ? 'checked="checked"' : ''; ?>/>
-                Participants: Send new activity notice&nbsp;
+                <?php echo __('Participants: Send new activity notice'); ?>&nbsp;
                 <i class="help-tip icon-question-sign" href="#new_message_for_participants"></i>
                 </div>
             </td>
         </tr>
         <tr>
-            <td width="160">Overlimit Notice:</td>
+            <td width="160"><?php echo __('Overlimit Notice'); ?>:</td>
             <td>
                 <input type="checkbox" name="overlimit_notice_active" <?php
 echo $config['overlimit_notice_active'] ? 'checked="checked"' : ''; ?>/>
-                Ticket Submitter&nbsp;
+                <?php echo __('Ticket Submitter'); ?>&nbsp;
                 <i class="help-tip icon-question-sign" href="#overlimit_notice"></i>
             </td>
         </tr>
     </tbody>
 </table>
 <p style="padding-left:200px;">
-    <input class="button" type="submit" name="submit" value="Save Changes">
-    <input class="button" type="reset" name="reset" value="Reset Changes">
+    <input class="button" type="submit" name="submit" value="<?php echo __('Save Changes'); ?>">
+    <input class="button" type="reset" name="reset" value="<?php echo __('Reset Changes'); ?>">
 </p>
 </form>
diff --git a/include/staff/settings-emails.inc.php b/include/staff/settings-emails.inc.php
index 61535604a7ce198eb1d722ff83c98fc39ebb2535..228791074b65c01b5afd8d3ca254a71ee12d69e9 100644
--- a/include/staff/settings-emails.inc.php
+++ b/include/staff/settings-emails.inc.php
@@ -1,7 +1,7 @@
 <?php
 if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config) die('Access Denied');
 ?>
-<h2>Email Settings and Options</h2>
+<h2><?php echo __('Email Settings and Options');?></h2>
 <form action="settings.php?t=emails" method="post" id="save">
 <?php csrf_token(); ?>
 <input type="hidden" name="t" value="emails" >
@@ -9,17 +9,17 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
     <thead>
         <tr>
             <th colspan="2">
-                <h4>Email Settings</h4>
-                <em>Note that some of the global settings can be overridden at department/email level.</em>
+                <h4><?php echo __('Email Settings');?></h4>
+                <em><?php echo __('Note that some of the global settings can be overwridden at department/email level.');?></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
-            <td width="180" class="required">Default Template Set:</td>
+            <td width="180" class="required"><?php echo __('Default Template Set'); ?>:</td>
             <td>
                 <select name="default_template_id">
-                    <option value="">&mdash; Select Default Email Template Set &mdash;</option>
+                    <option value="">&mdash; <?php echo __('Select Default Email Template Set'); ?> &mdash;</option>
                     <?php
                     $sql='SELECT tpl_id, name FROM '.EMAIL_TEMPLATE_GRP_TABLE
                         .' WHERE isactive =1 ORDER BY name';
@@ -35,10 +35,10 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
             </td>
         </tr>
         <tr>
-            <td width="180" class="required">Default System Email:</td>
+            <td width="180" class="required"><?php echo __('Default System Email');?>:</td>
             <td>
                 <select name="default_email_id">
-                    <option value=0 disabled>Select One</option>
+                    <option value=0 disabled><?php echo __('Select One');?></option>
                     <?php
                     $sql='SELECT email_id,email,name FROM '.EMAIL_TABLE;
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -55,10 +55,10 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
             </td>
         </tr>
         <tr>
-            <td width="180" class="required">Default Alert Email:</td>
+            <td width="180" class="required"><?php echo __('Default Alert Email');?>:</td>
             <td>
                 <select name="alert_email_id">
-                    <option value="0" selected="selected">Use Default System Email (above)</option>
+                    <option value="0" selected="selected"><?php echo __('Use Default System Email (above)');?></option>
                     <?php
                     $sql='SELECT email_id,email,name FROM '.EMAIL_TABLE.' WHERE email_id != '.db_input($config['default_email_id']);
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -75,66 +75,69 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
             </td>
         </tr>
         <tr>
-            <td width="180" class="required">Admin's Email Address:</td>
+            <td width="180" class="required"><?php echo __("Admin's Email Address");?>:</td>
             <td>
                 <input type="text" size=40 name="admin_email" value="<?php echo $config['admin_email']; ?>">
                     &nbsp;<font class="error">*&nbsp;<?php echo $errors['admin_email']; ?></font>
                 <i class="help-tip icon-question-sign" href="#admins_email_address"></i>
             </td>
         </tr>
-        <tr><th colspan=2><em><strong>Incoming Emails:</strong>&nbsp;
+        <tr><th colspan=2><em><strong><?php echo __('Incoming Emails'); ?>:</strong>&nbsp;
             </em></th>
         <tr>
-            <td width="180">Email Fetching:</td>
-            <td><input type="checkbox" name="enable_mail_polling" value=1 <?php echo $config['enable_mail_polling']? 'checked="checked"': ''; ?>  > Enable
+            <td width="180"><?php echo __('Email Fetching'); ?>:</td>
+            <td><input type="checkbox" name="enable_mail_polling" value=1 <?php echo $config['enable_mail_polling']? 'checked="checked"': ''; ?>>
+                <?php echo __('Enable'); ?>
                 <i class="help-tip icon-question-sign" href="#email_fetching"></i>
                 &nbsp;
                  <input type="checkbox" name="enable_auto_cron" <?php echo $config['enable_auto_cron']?'checked="checked"':''; ?>>
-                 Fetch on auto-cron&nbsp;
+                <?php echo __('Fetch on auto-cron'); ?>&nbsp;
                 <i class="help-tip icon-question-sign" href="#enable_autocron_fetch"></i>
             </td>
         </tr>
         <tr>
-            <td width="180">Strip Quoted Reply:</td>
+            <td width="180"><?php echo __('Strip Quoted Reply');?>:</td>
             <td>
                 <input type="checkbox" name="strip_quoted_reply" <?php echo $config['strip_quoted_reply'] ? 'checked="checked"':''; ?>>
-                Enable <i class="help-tip icon-question-sign" href="#strip_quoted_reply"></i>
+                <?php echo __('Enable'); ?>
+                <i class="help-tip icon-question-sign" href="#strip_quoted_reply"></i>
                 &nbsp;<font class="error">&nbsp;<?php echo $errors['strip_quoted_reply']; ?></font>
             </td>
         </tr>
         <tr>
-            <td width="180">Reply Separator Tag:</td>
+            <td width="180"><?php echo __('Reply Separator Tag');?>:</td>
             <td><input type="text" name="reply_separator" value="<?php echo $config['reply_separator']; ?>">
                 &nbsp;<font class="error">&nbsp;<?php echo $errors['reply_separator']; ?></font>&nbsp;<i class="help-tip icon-question-sign" href="#reply_separator_tag"></i>
             </td>
         </tr>
         <tr>
-            <td width="180">Emailed Tickets Priority:</td>
+            <td width="180"><?php echo __('Emailed Tickets Priority'); ?>:</td>
             <td>
-                <input type="checkbox" name="use_email_priority" value="1" <?php echo $config['use_email_priority'] ?'checked="checked"':''; ?> >&nbsp;Enable&nbsp;
+                <input type="checkbox" name="use_email_priority" value="1" <?php echo $config['use_email_priority'] ?'checked="checked"':''; ?>>
+                &nbsp;<?php echo __('Enable'); ?>&nbsp;
                 <i class="help-tip icon-question-sign" href="#emailed_tickets_priority"></i>
             </td>
         </tr>
         <tr>
-            <td width="180">Accept All Emails:</td>
+            <td width="180"><?php echo __('Accept All Emails'); ?>:</td>
             <td><input type="checkbox" name="accept_unregistered_email" <?php
                 echo $config['accept_unregistered_email'] ? 'checked="checked"' : ''; ?>/>
-                Accept email from unknown Users
+                <?php echo __('Accept email from unknown Users'); ?>
                 <i class="help-tip icon-question-sign" href="#accept_all_emails"></i>
             </td>
         </tr>
         <tr>
-            <td width="180">Accept Email Collaborators:</td>
+            <td width="180"><?php echo __('Accept Email Collaborators'); ?>:</td>
             <td><input type="checkbox" name="add_email_collabs" <?php
-    echo $config['add_email_collabs'] ? 'checked="checked"' : ''; ?>/>
-            Automatically add collaborators from email fields&nbsp;
+            echo $config['add_email_collabs'] ? 'checked="checked"' : ''; ?>/>
+            <?php echo __('Automatically add collaborators from email fields'); ?>&nbsp;
             <i class="help-tip icon-question-sign" href="#accept_email_collaborators"></i>
         </tr>
-        <tr><th colspan=2><em><strong>Outgoing Emails</strong>: Default email only applies to outgoing emails without SMTP setting.</em></th></tr>
-        <tr><td width="180">Default MTA:</td>
+        <tr><th colspan=2><em><strong><?php echo __('Outgoing Emails');?></strong>: <?php echo __('Default email only applies to outgoing emails without SMTP setting.');?></em></th></tr>
+        <tr><td width="180"><?php echo __('Default MTA'); ?>:</td>
             <td>
                 <select name="default_smtp_id">
-                    <option value=0 selected="selected">None: Use PHP mail function</option>
+                    <option value=0 selected="selected"><?php echo __('None: Use PHP mail function');?></option>
                     <?php
                     $sql=' SELECT email_id, email, name, smtp_host '
                         .' FROM '.EMAIL_TABLE.' WHERE smtp_active = 1';
@@ -153,7 +156,7 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
     </tbody>
 </table>
 <p style="padding-left:250px;">
-    <input class="button" type="submit" name="submit" value="Save Changes">
-    <input class="button" type="reset" name="reset" value="Reset Changes">
+    <input class="button" type="submit" name="submit" value="<?php echo __('Save Changes');?>">
+    <input class="button" type="reset" name="reset" value="<?php echo __('Reset Changes');?>">
 </p>
 </form>
diff --git a/include/staff/settings-kb.inc.php b/include/staff/settings-kb.inc.php
index 9f120a814aa493978b90709b4357c54451c124ba..cfac97418f92f1156c0c6022911dc4aaa3bd0310 100644
--- a/include/staff/settings-kb.inc.php
+++ b/include/staff/settings-kb.inc.php
@@ -1,40 +1,42 @@
-<?php
-if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config) die('Access Denied');
-?>
-<h2>Knowledge Base Settings and Options</h2>
-<form action="settings.php?t=kb" method="post" id="save">
-<?php csrf_token(); ?>
-<input type="hidden" name="t" value="kb" >
-<table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2">
-    <thead>
-        <tr>
-            <th colspan="2">
-                <h4>Knowledge Base Settings</h4>
-                <em>Disabling knowledge base disables clients' interface.</em>
-            </th>
-        </tr>
-    </thead>
-    <tbody>
-        <tr>
-            <td width="180">Knowledge Base Status:</td>
-            <td>
-              <input type="checkbox" name="enable_kb" value="1" <?php echo $config['enable_kb']?'checked="checked"':''; ?>>
-              Enable Knowledge Base
-              &nbsp;<font class="error">&nbsp;<?php echo $errors['enable_kb']; ?></font> <i class="help-tip icon-question-sign" href="#knowledge_base_status"></i>
-            </td>
-        </tr>
-        <tr>
-            <td width="180">Canned Responses:</td>
-            <td>
-                <input type="checkbox" name="enable_premade" value="1" <?php echo $config['enable_premade']?'checked="checked"':''; ?> >
-                Enable Canned Responses
-                &nbsp;<font class="error">&nbsp;<?php echo $errors['enable_premade']; ?></font> <i class="help-tip icon-question-sign" href="#canned_responses"></i>
-            </td>
-        </tr>
-    </tbody>
-</table>
-<p style="padding-left:210px;">
-    <input class="button" type="submit" name="submit" value="Save Changes">
-    <input class="button" type="reset" name="reset" value="Reset Changes">
-</p>
-</form>
+<?php
+if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config) die('Access Denied');
+?>
+<h2><?php echo __('Knowledge Base Settings and Options');?></h2>
+<form action="settings.php?t=kb" method="post" id="save">
+<?php csrf_token(); ?>
+<input type="hidden" name="t" value="kb" >
+<table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2">
+    <thead>
+        <tr>
+            <th colspan="2">
+                <h4><?php echo __('Knowledge Base Settings');?></h4>
+                <em><?php echo __("Disabling knowledge base disables clients' interface.");?></em>
+            </th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr>
+            <td width="180"><?php echo __('Knowledge Base Status'); ?>:</td>
+            <td>
+                <input type="checkbox" name="enable_kb" value="1" <?php echo $config['enable_kb']?'checked="checked"':''; ?>>
+                <?php echo __('Enable Knowledge Base'); ?>
+                &nbsp;<font class="error">&nbsp;<?php echo $errors['enable_kb']; ?></font>
+                <i class="help-tip icon-question-sign" href="#knowledge_base_status"></i>
+            </td>
+        </tr>
+        <tr>
+            <td width="180"><?php echo __('Canned Responses');?>:</td>
+            <td>
+                <input type="checkbox" name="enable_premade" value="1" <?php echo $config['enable_premade']?'checked="checked"':''; ?> >
+                <?php echo __('Enable Canned Responses'); ?>
+                &nbsp;<font class="error">&nbsp;<?php echo $errors['enable_premade']; ?></font>
+                <i class="help-tip icon-question-sign" href="#canned_responses"></i>
+            </td>
+        </tr>
+    </tbody>
+</table>
+<p style="padding-left:210px;">
+    <input class="button" type="submit" name="submit" value="<?php echo __('Save Changes'); ?>">
+    <input class="button" type="reset" name="reset" value="<?php echo __('Reset Changes'); ?>">
+</p>
+</form>
diff --git a/include/staff/settings-pages.inc.php b/include/staff/settings-pages.inc.php
index c92f441442823cfd279a4b21909d9267b44a3f91..9727bde4060beeaacce6169a375b87b0b5382aa9 100644
--- a/include/staff/settings-pages.inc.php
+++ b/include/staff/settings-pages.inc.php
@@ -2,7 +2,7 @@
 if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config) die('Access Denied');
 $pages = Page::getPages();
 ?>
-<h2>Company Profile</h2>
+<h2><?php echo __('Company Profile'); ?></h2>
 <form action="settings.php?t=pages" method="post" id="save"
     enctype="multipart/form-data">
 <?php csrf_token(); ?>
@@ -10,7 +10,7 @@ $pages = Page::getPages();
 <table class="form_table settings_table" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead><tr>
         <th colspan="2">
-            <h4>Basic Information</h4>
+            <h4><?php echo __('Basic Information'); ?></h4>
         </th>
     </tr></thead>
     <tbody>
@@ -23,18 +23,20 @@ $pages = Page::getPages();
     <thead>
         <tr>
             <th colspan="2">
-                <h4>Site Pages</h4>
-                <em>To edit or add new pages go to <a href="pages.php">Manage > Site Pages</a></em>
+                <h4><?php echo __('Site Pages'); ?></h4>
+                <em><?php echo sprintf(__(
+                'To edit or add new pages go to %s Manage &gt; Site Pages %s'),
+                '<a href="pages.php">','</a>'); ?></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
-            <td width="220" class="required">Landing Page:</td>
+            <td width="220" class="required"><?php echo __('Landing Page'); ?>:</td>
             <td>
                 <span>
                 <select name="landing_page_id">
-                    <option value="">&mdash; Select Landing Page &mdash;</option>
+                    <option value="">&mdash; <?php echo __('Select Landing Page'); ?> &mdash;</option>
                     <?php
                     foreach($pages as $page) {
                         if(strcasecmp($page->getType(), 'landing')) continue;
@@ -49,11 +51,12 @@ $pages = Page::getPages();
             </td>
         </tr>
         <tr>
-            <td width="220" class="required">Offline Page:</td>
+            <td width="220" class="required"><?php echo __('Offline Page'); ?>:</td>
             <td>
                 <span>
                 <select name="offline_page_id">
-                    <option value="">&mdash; Select Offline Page &mdash;</option>
+                    <option value="">&mdash; <?php echo __('Select Offline Page');
+                        ?> &mdash;</option>
                     <?php
                     foreach($pages as $page) {
                         if(strcasecmp($page->getType(), 'offline')) continue;
@@ -68,11 +71,13 @@ $pages = Page::getPages();
             </td>
         </tr>
         <tr>
-            <td width="220" class="required">Default Thank-You Page:</td>
+            <td width="220" class="required"><?php
+                echo __('Default Thank-You Page'); ?>:</td>
             <td>
                 <span>
                 <select name="thank-you_page_id">
-                    <option value="">&mdash; Select Thank-You Page &mdash;</option>
+                    <option value="">&mdash; <?php
+                        echo __('Select Thank-You Page'); ?> &mdash;</option>
                     <?php
                     foreach($pages as $page) {
                         if(strcasecmp($page->getType(), 'thank-you')) continue;
@@ -92,10 +97,10 @@ $pages = Page::getPages();
     <thead>
         <tr>
             <th colspan="2">
-                <h4>Logos
+                <h4><?php echo __('Logos'); ?>
                     <i class="help-tip icon-question-sign" href="#logos"></i>
                     </h4>
-                <em>System Default Logo</em>
+                <em><?php echo __('System Default Logo'); ?></em>
             </th>
         </tr>
     </thead>
@@ -116,7 +121,7 @@ $pages = Page::getPages();
         </td></tr>
         <tr>
             <th colspan="2">
-                <em>Use a custom logo&nbsp;<i class="help-tip icon-question-sign" href="#upload_a_new_logo"></i></em>
+                <em><?php echo __('Use a custom logo'); ?>&nbsp;<i class="help-tip icon-question-sign" href="#upload_a_new_logo"></i></em>
             </th>
         </tr>
         <tr><td colspan="2">
@@ -139,13 +144,13 @@ $pages = Page::getPages();
                 <?php if ($logo->getId() != $current) { ?>
                 <label>
                 <input type="checkbox" name="delete-logo[]" value="<?php
-                    echo $logo->getId(); ?>"/> Delete
+                    echo $logo->getId(); ?>"/> <?php echo __('Delete'); ?>
                 </label>
                 <?php } ?>
                 </div>
             <?php } ?>
             <br/>
-            <b>Upload a new logo:</b>
+            <b><?php echo __('Upload a new logo'); ?>:</b>
             <input type="file" name="logo[]" size="30" value="" />
             <font class="error"><br/><?php echo $errors['logo']; ?></font>
         </td>
@@ -153,28 +158,31 @@ $pages = Page::getPages();
     </tbody>
 </table>
 <p style="padding-left:250px;">
-    <input class="button" type="submit" name="submit-button" value="Save Changes">
-    <input class="button" type="reset" name="reset" value="Reset Changes">
+    <input class="button" type="submit" name="submit-button" value="<?php
+    echo __('Save Changes'); ?>">
+    <input class="button" type="reset" name="reset" value="<?php
+    echo __('Reset Changes'); ?>">
 </p>
 </form>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm'); ?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected
-        logos?</strong></font>
-        <br/><br/>Deleted logos CANNOT be recovered.
+        <font color="red"><strong><?php echo sprintf(
+        __('Are you sure you want to DELETE %s?'),
+        _N('selected logo', 'selected logos', 2)); ?></strong></font>
+        <br/><br/><?php echo __('Deleted data CANNOT be recovered.'); ?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.'); ?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel'); ?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!'); ?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/settings-system.inc.php b/include/staff/settings-system.inc.php
index f7eb33822e559e33aad48c87e357f65a041c52f6..30efbb4c5d084cbd44c5f4a48b915bf39e6afb11 100644
--- a/include/staff/settings-system.inc.php
+++ b/include/staff/settings-system.inc.php
@@ -3,7 +3,7 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
 
 $gmtime = Misc::gmtime();
 ?>
-<h2>System Settings and Preferences - <span>osTicket (<?php echo $cfg->getVersion(); ?>)</span></h2>
+<h2><?php echo __('System Settings and Preferences');?> - <span class="ltr">osTicket (<?php echo $cfg->getVersion(); ?>)</span></h2>
 <form action="settings.php?t=system" method="post" id="save">
 <?php csrf_token(); ?>
 <input type="hidden" name="t" value="system" >
@@ -11,26 +11,26 @@ $gmtime = Misc::gmtime();
     <thead>
         <tr>
             <th colspan="2">
-                <h4>System Settings &amp; Preferences</h4>
-                <em><b>General Settings</b></em>
+                <h4><?php echo __('System Settings and Preferences'); ?></h4>
+                <em><b><?php echo __('General Settings'); ?></b></em>
             </th>
         </tr>
     </thead>
     <tbody>
 
         <tr>
-            <td width="220" class="required">Helpdesk Status:</td>
+            <td width="220" class="required"><?php echo __('Helpdesk Status');?>:</td>
             <td>
                 <span>
-                <label><input type="radio" name="isonline"  value="1"   <?php echo $config['isonline']?'checked="checked"':''; ?> />&nbsp;<b>Online</b>&nbsp;</label>
-                <label><input type="radio" name="isonline"  value="0"   <?php echo !$config['isonline']?'checked="checked"':''; ?> />&nbsp;<b>Offline</b></label>
-                &nbsp;<font class="error"><?php echo $config['isoffline']?'osTicket offline':''; ?></font>
+                <label><input type="radio" name="isonline"  value="1"   <?php echo $config['isonline']?'checked="checked"':''; ?> />&nbsp;<b><?php echo __('Online'); ?></b>&nbsp;</label>
+                <label><input type="radio" name="isonline"  value="0"   <?php echo !$config['isonline']?'checked="checked"':''; ?> />&nbsp;<b><?php echo __('Offline'); ?></b></label>
+                &nbsp;<font class="error"><?php echo $config['isoffline']?'osTicket '.__('Offline'):''; ?></font>
                 <i class="help-tip icon-question-sign" href="#helpdesk_status"></i>
                 </span>
             </td>
         </tr>
         <tr>
-            <td width="220" class="required">Helpdesk URL:</td>
+            <td width="220" class="required"><?php echo __('Helpdesk URL');?>:</td>
             <td>
                 <input type="text" size="40" name="helpdesk_url" value="<?php echo $config['helpdesk_url']; ?>">
                 &nbsp;<font class="error">*&nbsp;<?php echo $errors['helpdesk_url']; ?></font>
@@ -38,23 +38,23 @@ $gmtime = Misc::gmtime();
         </td>
         </tr>
         <tr>
-            <td width="220" class="required">Helpdesk Name/Title:</td>
+            <td width="220" class="required"><?php echo __('Helpdesk Name/Title');?>:</td>
             <td><input type="text" size="40" name="helpdesk_title" value="<?php echo $config['helpdesk_title']; ?>">
                 &nbsp;<font class="error">*&nbsp;<?php echo $errors['helpdesk_title']; ?></font>
                 <i class="help-tip icon-question-sign" href="#helpdesk_name_title"></i>
             </td>
         </tr>
         <tr>
-            <td width="220" class="required">Default Department:</td>
+            <td width="220" class="required"><?php echo __('Default Department');?>:</td>
             <td>
                 <select name="default_dept_id">
-                    <option value="">&mdash; Select Default Department &mdash;</option>
+                    <option value="">&mdash; <?php echo __('Select Default Department');?> &mdash;</option>
                     <?php
                     $sql='SELECT dept_id,dept_name FROM '.DEPT_TABLE.' WHERE ispublic=1';
                     if(($res=db_query($sql)) && db_num_rows($res)){
                         while (list($id, $name) = db_fetch_row($res)){
                             $selected = ($config['default_dept_id']==$id)?'selected="selected"':''; ?>
-                            <option value="<?php echo $id; ?>"<?php echo $selected; ?>><?php echo $name; ?> Dept</option>
+                            <option value="<?php echo $id; ?>"<?php echo $selected; ?>><?php echo $name; ?> <?php echo __('Dept');?></option>
                         <?php
                         }
                     } ?>
@@ -63,7 +63,7 @@ $gmtime = Misc::gmtime();
             </td>
         </tr>
 
-        <tr><td>Default Page Size:</td>
+        <tr><td><?php echo __('Default Page Size');?>:</td>
             <td>
                 <select name="max_page_size">
                     <?php
@@ -78,28 +78,28 @@ $gmtime = Misc::gmtime();
             </td>
         </tr>
         <tr>
-            <td>Default Log Level:</td>
+            <td><?php echo __('Default Log Level');?>:</td>
             <td>
                 <select name="log_level">
-                    <option value=0 <?php echo $config['log_level'] == 0 ? 'selected="selected"':''; ?>>None (Disable Logger)</option>
-                    <option value=3 <?php echo $config['log_level'] == 3 ? 'selected="selected"':''; ?>> DEBUG</option>
-                    <option value=2 <?php echo $config['log_level'] == 2 ? 'selected="selected"':''; ?>> WARN</option>
-                    <option value=1 <?php echo $config['log_level'] == 1 ? 'selected="selected"':''; ?>> ERROR</option>
+                    <option value=0 <?php echo $config['log_level'] == 0 ? 'selected="selected"':''; ?>><?php echo __('None (Disable Logger)');?></option>
+                    <option value=3 <?php echo $config['log_level'] == 3 ? 'selected="selected"':''; ?>> <?php echo __('DEBUG');?></option>
+                    <option value=2 <?php echo $config['log_level'] == 2 ? 'selected="selected"':''; ?>> <?php echo __('WARN');?></option>
+                    <option value=1 <?php echo $config['log_level'] == 1 ? 'selected="selected"':''; ?>> <?php echo __('ERROR');?></option>
                 </select>
                 <font class="error">&nbsp;<?php echo $errors['log_level']; ?></font>
                 <i class="help-tip icon-question-sign" href="#default_log_level"></i>
             </td>
         </tr>
         <tr>
-            <td>Purge Logs:</td>
+            <td><?php echo __('Purge Logs');?>:</td>
             <td>
                 <select name="log_graceperiod">
-                    <option value=0 selected>Never Purge Logs</option>
+                    <option value=0 selected><?php echo __('Never Purge Logs');?></option>
                     <?php
                     for ($i = 1; $i <=12; $i++) {
                         ?>
                         <option <?php echo $config['log_graceperiod']==$i?'selected="selected"':''; ?> value="<?php echo $i; ?>">
-                            After&nbsp;<?php echo $i; ?>&nbsp;<?php echo ($i>1)?'Months':'Month'; ?></option>
+                            <?php echo __('After');?>&nbsp;<?php echo $i; ?>&nbsp;<?php echo ($i>1)?__('Months'):__('Month'); ?></option>
                         <?php
                     } ?>
                 </select>
@@ -107,14 +107,14 @@ $gmtime = Misc::gmtime();
             </td>
         </tr>
         <tr>
-            <td width="180">Default Name Formatting:</td>
+            <td width="180"><?php echo __('Default Name Formatting'); ?>:</td>
             <td>
                 <select name="name_format">
 <?php foreach (PersonsName::allFormats() as $n=>$f) {
     list($desc, $func) = $f;
     $selected = ($config['name_format'] == $n) ? 'selected="selected"' : ''; ?>
                     <option value="<?php echo $n; ?>" <?php echo $selected;
-                        ?>><?php echo $desc; ?></option>
+                        ?>><?php echo __($desc); ?></option>
 <?php } ?>
                 </select>
                 <i class="help-tip icon-question-sign" href="#default_name_formatting"></i>
@@ -122,39 +122,39 @@ $gmtime = Misc::gmtime();
         </tr>
         <tr>
             <th colspan="2">
-                <em><b>Date and Time Options</b>&nbsp;
+                <em><b><?php echo __('Date and Time Options'); ?></b>&nbsp;
                 <i class="help-tip icon-question-sign" href="#date_time_options"></i>
                 </em>
             </th>
         </tr>
-        <tr><td width="220" class="required">Time Format:</td>
+        <tr><td width="220" class="required"><?php echo __('Time Format');?>:</td>
             <td>
                 <input type="text" name="time_format" value="<?php echo $config['time_format']; ?>">
                     &nbsp;<font class="error">*&nbsp;<?php echo $errors['time_format']; ?></font>
                     <em><?php echo Format::date($config['time_format'], $gmtime, $config['tz_offset'], $config['enable_daylight_saving']); ?></em></td>
         </tr>
-        <tr><td width="220" class="required">Date Format:</td>
+        <tr><td width="220" class="required"><?php echo __('Date Format');?>:</td>
             <td><input type="text" name="date_format" value="<?php echo $config['date_format']; ?>">
                         &nbsp;<font class="error">*&nbsp;<?php echo $errors['date_format']; ?></font>
                         <em><?php echo Format::date($config['date_format'], $gmtime, $config['tz_offset'], $config['enable_daylight_saving']); ?></em>
             </td>
         </tr>
-        <tr><td width="220" class="required">Date &amp; Time Format:</td>
+        <tr><td width="220" class="required"><?php echo __('Date and Time Format');?>:</td>
             <td><input type="text" name="datetime_format" value="<?php echo $config['datetime_format']; ?>">
                         &nbsp;<font class="error">*&nbsp;<?php echo $errors['datetime_format']; ?></font>
                         <em><?php echo Format::date($config['datetime_format'], $gmtime, $config['tz_offset'], $config['enable_daylight_saving']); ?></em>
             </td>
         </tr>
-        <tr><td width="220" class="required">Day, Date &amp; Time Format:</td>
+        <tr><td width="220" class="required"><?php echo __('Day, Date and Time Format');?>:</td>
             <td><input type="text" name="daydatetime_format" value="<?php echo $config['daydatetime_format']; ?>">
                         &nbsp;<font class="error">*&nbsp;<?php echo $errors['daydatetime_format']; ?></font>
                         <em><?php echo Format::date($config['daydatetime_format'], $gmtime, $config['tz_offset'], $config['enable_daylight_saving']); ?></em>
             </td>
         </tr>
-        <tr><td width="220" class="required">Default Time Zone:</td>
+        <tr><td width="220" class="required"><?php echo __('Default Time Zone');?>:</td>
             <td>
                 <select name="default_timezone_id">
-                    <option value="">&mdash; Select Default Time Zone &mdash;</option>
+                    <option value="">&mdash; <?php echo __('Select Default Time Zone');?> &mdash;</option>
                     <?php
                     $sql='SELECT id, offset,timezone FROM '.TIMEZONE_TABLE.' ORDER BY id';
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -169,15 +169,15 @@ $gmtime = Misc::gmtime();
             </td>
         </tr>
         <tr>
-            <td width="220">Daylight Saving:</td>
+            <td width="220"><?php echo __('Daylight Saving');?>:</td>
             <td>
-                <input type="checkbox" name="enable_daylight_saving" <?php echo $config['enable_daylight_saving'] ? 'checked="checked"': ''; ?>>Observe daylight savings
+                <input type="checkbox" name="enable_daylight_saving" <?php echo $config['enable_daylight_saving'] ? 'checked="checked"': ''; ?>><?php echo __('Observe daylight savings');?>
             </td>
         </tr>
     </tbody>
 </table>
 <p style="padding-left:250px;">
-    <input class="button" type="submit" name="submit" value="Save Changes">
-    <input class="button" type="reset" name="reset" value="Reset Changes">
+    <input class="button" type="submit" name="submit" value="<?php echo __('Save Changes');?>">
+    <input class="button" type="reset" name="reset" value="<?php echo __('Reset Changes');?>">
 </p>
 </form>
diff --git a/include/staff/settings-tickets.inc.php b/include/staff/settings-tickets.inc.php
index a7960a02b16552d72bd5377a138ccc049b535d46..2e23da95068b404e91ace8b02749690fb2b9a802 100644
--- a/include/staff/settings-tickets.inc.php
+++ b/include/staff/settings-tickets.inc.php
@@ -3,7 +3,7 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config)
 if(!($maxfileuploads=ini_get('max_file_uploads')))
     $maxfileuploads=DEFAULT_MAX_FILE_UPLOADS;
 ?>
-<h2>Ticket Settings and Options</h2>
+<h2><?php echo __('Ticket Settings and Options');?></h2>
 <form action="settings.php?t=tickets" method="post" id="save">
 <?php csrf_token(); ?>
 <input type="hidden" name="t" value="tickets" >
@@ -11,46 +11,81 @@ if(!($maxfileuploads=ini_get('max_file_uploads')))
     <thead>
         <tr>
             <th colspan="2">
-                <h4>Global Ticket Settings</h4>
-                <em>System-wide default ticket settings and options.</em>
+                <h4><?php echo __('Global Ticket Settings');?></h4>
+                <em><?php echo __('System-wide default ticket settings and options.'); ?></em>
             </th>
         </tr>
     </thead>
     <tbody>
-        <tr><td width="220" class="required">Ticket IDs:</td>
+        <tr>
+            <td>
+                <?php echo __('Default Ticket Number Format'); ?>:
+            </td>
             <td>
-                <input type="radio" name="random_ticket_ids"  value="0" <?php echo !$config['random_ticket_ids']?'checked="checked"':''; ?> />
-                Sequential
-                <input type="radio" name="random_ticket_ids"  value="1" <?php echo $config['random_ticket_ids']?'checked="checked"':''; ?> />
-                Random
+                <input type="text" name="number_format" value="<?php echo $config['number_format']; ?>"/>
+                <span class="faded"><?php echo __('e.g.'); ?> <span id="format-example"><?php
+                    if ($config['sequence_id'])
+                        $seq = Sequence::lookup($config['sequence_id']);
+                    if (!isset($seq))
+                        $seq = new RandomSequence();
+                    echo $seq->current($config['number_format']);
+                    ?></span></span>
+                <i class="help-tip icon-question-sign" href="#number_format"></i>
+                <div class="error"><?php echo $errors['number_format']; ?></div>
+            </td>
+        </tr>
+        <tr><td width="220"><?php echo __('Default Ticket Number Sequence'); ?>:</td>
+<?php $selected = 'selected="selected"'; ?>
+            <td>
+                <select name="sequence_id">
+                <option value="0" <?php if ($config['sequence_id'] == 0) echo $selected;
+                    ?>>&mdash; <?php echo __('Random'); ?> &mdash;</option>
+<?php foreach (Sequence::objects() as $s) { ?>
+                <option value="<?php echo $s->id; ?>" <?php
+                    if ($config['sequence_id'] == $s->id) echo $selected;
+                    ?>><?php echo $s->name; ?></option>
+<?php } ?>
+                </select>
+                <button class="action-button pull-right" onclick="javascript:
+                $.dialog('ajax.php/sequence/manage', 205);
+                return false;
+                "><i class="icon-gear"></i> <?php echo __('Manage'); ?></button>
+                <i class="help-tip icon-question-sign" href="#sequence_id"></i>
             </td>
         </tr>
-
         <tr>
             <td width="180" class="required">
-                Default SLA:
+                <?php echo __('Default Status'); ?>:
             </td>
             <td>
                 <span>
-                <select name="default_sla_id">
-                    <option value="0">&mdash; None &mdash;</option>
-                    <?php
-                    if($slas=SLA::getSLAs()) {
-                        foreach($slas as $id => $name) {
-                            echo sprintf('<option value="%d" %s>%s</option>',
-                                    $id,
-                                    ($config['default_sla_id'] && $id==$config['default_sla_id'])?'selected="selected"':'',
-                                    $name);
-                        }
-                    }
-                    ?>
+                <select name="default_ticket_status_id">
+                <?php
+                $criteria = array('states' => array('open'));
+                foreach (TicketStatusList::getStatuses($criteria) as $status) {
+                    $name = $status->getName();
+                    if (!($isenabled = $status->isEnabled()))
+                        $name.=' '.__('(disabled)');
+
+                    echo sprintf('<option value="%d" %s %s>%s</option>',
+                            $status->getId(),
+                            ($config['default_ticket_status_id'] ==
+                             $status->getId() && $isenabled)
+                             ? 'selected="selected"' : '',
+                             $isenabled ? '' : 'disabled="disabled"',
+                             $name
+                            );
+                }
+                ?>
                 </select>
-                &nbsp;<span class="error">*&nbsp;<?php echo $errors['default_sla_id']; ?></span>  <i class="help-tip icon-question-sign" href="#default_sla"></i>
+                &nbsp;
+                <span class="error">*&nbsp;<?php echo $errors['default_ticket_status_id']; ?></span>
+                <i class="help-tip icon-question-sign" href="#default_ticket_status"></i>
                 </span>
             </td>
         </tr>
         <tr>
-            <td width="180" class="required">Default Priority:</td>
+            <td width="180" class="required"><?php echo __('Default Priority');?>:</td>
             <td>
                 <select name="default_priority_id">
                     <?php
@@ -64,10 +99,33 @@ if(!($maxfileuploads=ini_get('max_file_uploads')))
              </td>
         </tr>
         <tr>
-            <td width="180">Default Help Topic:</td>
+            <td width="180" class="required">
+                <?php echo __('Default SLA');?>:
+            </td>
+            <td>
+                <span>
+                <select name="default_sla_id">
+                    <option value="0">&mdash; <?php echo __('None');?> &mdash;</option>
+                    <?php
+                    if($slas=SLA::getSLAs()) {
+                        foreach($slas as $id => $name) {
+                            echo sprintf('<option value="%d" %s>%s</option>',
+                                    $id,
+                                    ($config['default_sla_id'] && $id==$config['default_sla_id'])?'selected="selected"':'',
+                                    $name);
+                        }
+                    }
+                    ?>
+                </select>
+                &nbsp;<span class="error">*&nbsp;<?php echo $errors['default_sla_id']; ?></span>  <i class="help-tip icon-question-sign" href="#default_sla"></i>
+                </span>
+            </td>
+        </tr>
+        <tr>
+            <td width="180"><?php echo __('Default Help Topic'); ?>:</td>
             <td>
                 <select name="default_help_topic">
-                    <option value="0">&mdash; None &mdash;</option><?php
+                    <option value="0">&mdash; <?php echo __('None'); ?> &mdash;</option><?php
                     $topics = Topic::getHelpTopics(false, Topic::DISPLAY_DISABLED);
                     while (list($id,$topic) = each($topics)) { ?>
                         <option value="<?php echo $id; ?>"<?php echo ($config['default_help_topic']==$id)?'selected':''; ?>><?php echo $topic; ?></option>
@@ -78,140 +136,105 @@ if(!($maxfileuploads=ini_get('max_file_uploads')))
             </td>
         </tr>
         <tr>
-            <td>Maximum <b>Open</b> Tickets:</td>
+            <td><?php echo __('Maximum <b>Open</b> Tickets');?>:</td>
             <td>
                 <input type="text" name="max_open_tickets" size=4 value="<?php echo $config['max_open_tickets']; ?>">
-                per email/user. <i class="help-tip icon-question-sign" href="#maximum_open_tickets"></i>
+                <?php echo __('per end user'); ?> <i class="help-tip icon-question-sign" href="#maximum_open_tickets"></i>
             </td>
         </tr>
         <tr>
-            <td>Agent Collision Avoidance Duration:</td>
+            <td><?php echo __('Agent Collision Avoidance Duration'); ?>:</td>
             <td>
                 <input type="text" name="autolock_minutes" size=4 value="<?php echo $config['autolock_minutes']; ?>">
-                <font class="error"><?php echo $errors['autolock_minutes']; ?></font>&nbsp;minutes&nbsp;<i class="help-tip icon-question-sign" href="#agent_collision_avoidance"></i>
+                <font class="error"><?php echo $errors['autolock_minutes']; ?></font>&nbsp;<?php echo __('minutes'); ?>&nbsp;<i class="help-tip icon-question-sign" href="#agent_collision_avoidance"></i>
             </td>
         </tr>
         <tr>
-            <td>Human Verification:</td>
+            <td><?php echo __('Human Verification');?>:</td>
             <td>
                 <input type="checkbox" name="enable_captcha" <?php echo $config['enable_captcha']?'checked="checked"':''; ?>>
-                Enable CAPTCHA on new web tickets. &nbsp;<font class="error">&nbsp;<?php echo $errors['enable_captcha']; ?></font>&nbsp;<i class="help-tip icon-question-sign" href="#human_verification"></i>
+                <?php echo __('Enable CAPTCHA on new web tickets.');?>
+                &nbsp;<font class="error">&nbsp;<?php echo $errors['enable_captcha']; ?></font>
+                &nbsp;<i class="help-tip icon-question-sign" href="#human_verification"></i>
             </td>
         </tr>
         <tr>
-            <td>Claim on Response:</td>
+            <td><?php echo __('Claim on Response'); ?>:</td>
             <td>
                 <input type="checkbox" name="auto_claim_tickets" <?php echo $config['auto_claim_tickets']?'checked="checked"':''; ?>>
-                Enable&nbsp;<i class="help-tip icon-question-sign" href="#claim_tickets"></i>
+                <?php echo __('Enable'); ?>&nbsp;<i class="help-tip icon-question-sign" href="#claim_tickets"></i>
             </td>
         </tr>
         <tr>
-            <td>Assigned Tickets:</td>
+            <td><?php echo __('Assigned Tickets');?>:</td>
             <td>
                 <input type="checkbox" name="show_assigned_tickets" <?php
                 echo !$config['show_assigned_tickets']?'checked="checked"':''; ?>>
-                Exclude assigned tickets from open queue. <i class="help-tip icon-question-sign" href="#assigned_tickets"></i>
+                <?php echo __('Exclude assigned tickets from open queue.'); ?>
+                <i class="help-tip icon-question-sign" href="#assigned_tickets"></i>
             </td>
         </tr>
         <tr>
-            <td>Answered Tickets:</td>
+            <td><?php echo __('Answered Tickets');?>:</td>
             <td>
                 <input type="checkbox" name="show_answered_tickets" <?php
                 echo !$config['show_answered_tickets']?'checked="checked"':''; ?>>
-                Exclude answered tickets from open queue. <i class="help-tip icon-question-sign" href="#answered_tickets"></i>
+                <?php echo __('Exclude answered tickets from open queue.'); ?>
+                <i class="help-tip icon-question-sign" href="#answered_tickets"></i>
             </td>
         </tr>
         <tr>
-            <td>Staff Identity Masking:</td>
+            <td><?php echo __('Agent Identity Masking'); ?>:</td>
             <td>
                 <input type="checkbox" name="hide_staff_name" <?php echo $config['hide_staff_name']?'checked="checked"':''; ?>>
-                Hide staff's name on responses. <i class="help-tip icon-question-sign" href="#staff_identity_masking"></i>
+                <?php echo __("Hide agent's name on responses."); ?>
+                <i class="help-tip icon-question-sign" href="#staff_identity_masking"></i>
             </td>
         </tr>
         <tr>
-            <td>Enable HTML Ticket Thread:</td>
+            <td><?php echo __('Enable HTML Ticket Thread'); ?>:</td>
             <td>
                 <input type="checkbox" name="enable_html_thread" <?php
                 echo $config['enable_html_thread']?'checked="checked"':''; ?>>
-                Enable rich text in ticket thread and autoresponse emails. <i class="help-tip icon-question-sign" href="#enable_html_ticket_thread"></i>
+                <?php echo __('Enable rich text in ticket thread and autoresponse emails.'); ?>
+                <i class="help-tip icon-question-sign" href="#enable_html_ticket_thread"></i>
             </td>
         </tr>
         <tr>
-            <td>Allow Client Updates:</td>
+            <td><?php echo __('Allow Client Updates'); ?>:</td>
             <td>
                 <input type="checkbox" name="allow_client_updates" <?php
                 echo $config['allow_client_updates']?'checked="checked"':''; ?>>
-                Allow clients to update ticket details via the web portal
+                <?php echo __('Allow clients to update ticket details via the web portal'); ?>
             </td>
         </tr>
         <tr>
             <th colspan="2">
-                <em><b>Attachments</b>:  Size and max. uploads setting mainly apply to web tickets.</em>
+                <em><b><?php echo __('Attachments');?></b>:  <?php echo __('Size and maximum uploads setting mainly apply to web tickets.');?></em>
             </th>
         </tr>
         <tr>
-            <td width="180">Allow Attachments:</td>
+            <td width="180"><?php echo __('Ticket Attachment Settings');?>:</td>
             <td>
-              <input type="checkbox" name="allow_attachments" <?php echo
-              $config['allow_attachments']?'checked="checked"':''; ?>> <b>Allow Attachments</b>
-                &nbsp; <em>(Global Setting)</em>
-                &nbsp;<font class="error">&nbsp;<?php echo $errors['allow_attachments']; ?></font>
-            </td>
-        </tr>
-        <tr>
-            <td width="180">Emailed/API Attachments:</td>
-            <td>
-                <input type="checkbox" name="allow_email_attachments" <?php echo $config['allow_email_attachments']?'checked="checked"':''; ?>> Accept emailed/API attachments.
-                    &nbsp;<font class="error">&nbsp;<?php echo $errors['allow_email_attachments']; ?></font>
-            </td>
-        </tr>
-        <tr>
-            <td width="180">Online/Web Attachments:</td>
-            <td>
-                <input type="checkbox" name="allow_online_attachments" <?php echo $config['allow_online_attachments']?'checked="checked"':''; ?> >
-                    Allow web upload &nbsp;&nbsp;&nbsp;&nbsp;
-                <input type="checkbox" name="allow_online_attachments_onlogin" <?php echo $config['allow_online_attachments_onlogin'] ?'checked="checked"':''; ?> >
-                    Limit to authenticated users only. <em>(User must be logged in to upload files)</em>
-                    <font class="error">&nbsp;<?php echo $errors['allow_online_attachments']; ?></font>
-            </td>
-        </tr>
-        <tr>
-            <td>Max. User File Uploads:</td>
-            <td>
-                <select name="max_user_file_uploads">
-                    <?php
-                    for($i = 1; $i <=$maxfileuploads; $i++) {
-                        ?>
-                        <option <?php echo $config['max_user_file_uploads']==$i?'selected="selected"':''; ?> value="<?php echo $i; ?>">
-                            <?php echo $i; ?>&nbsp;<?php echo ($i>1)?'files':'file'; ?></option>
-                        <?php
-                    } ?>
-                </select>
-                <em>(Number of files the user is allowed to upload simultaneously)</em>
-                &nbsp;<font class="error">&nbsp;<?php echo $errors['max_user_file_uploads']; ?></font>
-            </td>
-        </tr>
-        <tr>
-            <td>Max. Staff File Uploads:</td>
-            <td>
-                <select name="max_staff_file_uploads">
-                    <?php
-                    for($i = 1; $i <=$maxfileuploads; $i++) {
-                        ?>
-                        <option <?php echo $config['max_staff_file_uploads']==$i?'selected="selected"':''; ?> value="<?php echo $i; ?>">
-                            <?php echo $i; ?>&nbsp;<?php echo ($i>1)?'files':'file'; ?></option>
-                        <?php
-                    } ?>
-                </select>
-                <em>(Number of files the staff is allowed to upload simultaneously)</em>
-                &nbsp;<font class="error">&nbsp;<?php echo $errors['max_staff_file_uploads']; ?></font>
+<?php
+                $tform = TicketForm::objects()->one()->getForm();
+                $f = $tform->getField('message');
+?>
+                <a class="action-button field-config" style="overflow:inherit"
+                    href="#ajax.php/form/field-config/<?php
+                        echo $f->get('id'); ?>"
+                    onclick="javascript:
+                        $.dialog($(this).attr('href').substr(1), [201]);
+                        return false;
+                    "><i class="icon-edit"></i> <?php echo __('Config'); ?></a>
+                <i class="help-tip icon-question-sign" href="#ticket_attachment_settings"></i>
             </td>
         </tr>
         <tr>
-            <td width="180">Maximum File Size:</td>
+            <td width="180"><?php echo __('Maximum File Size');?>:</td>
             <td>
                 <select name="max_file_size">
-                    <option value="262144">&mdash; Small &mdash;</option>
+                    <option value="262144">&mdash; <?php echo __('Small'); ?> &mdash;</option>
                     <?php $next = 512 << 10;
                     $max = strtoupper(ini_get('upload_max_filesize'));
                     $limit = (int) $max;
@@ -241,19 +264,22 @@ if(!($maxfileuploads=ini_get('max_file_uploads')))
                     }
                     ?>
                 </select>
-                <font class="error">&nbsp;<?php echo $errors['max_file_size']; ?></font>
+                <i class="help-tip icon-question-sign" href="#max_file_size"></i>
+                <div class="error"><?php echo $errors['max_file_size']; ?></div>
             </td>
         </tr>
         <tr>
-            <td width="180">Ticket Response Files:</td>
+            <td width="180"><?php echo __('Ticket Response Files');?>:</td>
             <td>
-                <input type="checkbox" name="email_attachments" <?php echo $config['email_attachments']?'checked="checked"':''; ?> > Email attachments to the user <i class="help-tip icon-question-sign" href="#ticket_response_files"></i>
+                <input type="checkbox" name="email_attachments" <?php echo $config['email_attachments']?'checked="checked"':''; ?>>
+                <?php echo __('Email attachments to the user'); ?>
+                <i class="help-tip icon-question-sign" href="#ticket_response_files"></i>
             </td>
         </tr>
         <?php if (($bks = FileStorageBackend::allRegistered())
                 && count($bks) > 1) { ?>
         <tr>
-            <td width="180">Store Attachments:</td>
+            <td width="180"><?php echo __('Store Attachments'); ?>:</td>
             <td><select name="default_storage_bk"><?php
                 foreach ($bks as $char=>$class) {
                     $selected = $config['default_storage_bk'] == $char
@@ -264,23 +290,25 @@ if(!($maxfileuploads=ini_get('max_file_uploads')))
             </td>
         </tr>
         <?php } ?>
-        <tr>
-            <th colspan="2">
-                <em><strong>Accepted File Types</strong>: Limit the type of files users are allowed to submit.
-                <font class="error">&nbsp;<?php echo $errors['allowed_filetypes']; ?></font></em>
-            </th>
-        </tr>
-        <tr>
-            <td colspan="2">
-                <em>Enter allowed file extensions separated by a comma. e.g .doc, .pdf. To accept all files enter wildcard <b><i>.*</i></b>&nbsp;i.e dotStar (NOT Recommended).</em><br>
-                <textarea name="allowed_filetypes" cols="21" rows="4" style="width: 65%;" wrap="hard" ><?php echo $config['allowed_filetypes']; ?></textarea>
-            </td>
-        </tr>
     </tbody>
 </table>
 <p style="padding-left:250px;">
-    <input class="button" type="submit" name="submit" value="Save Changes">
-    <input class="button" type="reset" name="reset" value="Reset Changes">
+    <input class="button" type="submit" name="submit" value="<?php echo __('Save Changes');?>">
+    <input class="button" type="reset" name="reset" value="<?php echo __('Reset Changes');?>">
 </p>
 </form>
-
+<script type="text/javascript">
+$(function() {
+    var request = null,
+      update_example = function() {
+      request && request.abort();
+      request = $.get('ajax.php/sequence/'
+        + $('[name=sequence_id] :selected').val(),
+        {'format': $('[name=number_format]').val()},
+        function(data) { $('#format-example').text(data); }
+      );
+    };
+    $('[name=sequence_id]').on('change', update_example);
+    $('[name=number_format]').on('keyup', update_example);
+});
+</script>
diff --git a/include/staff/slaplan.inc.php b/include/staff/slaplan.inc.php
index 7549453fe690c65acd51268da334d5e3df7a1ca9..2e72720b8f2cd8030b70e039a321177196eb282a 100644
--- a/include/staff/slaplan.inc.php
+++ b/include/staff/slaplan.inc.php
@@ -3,16 +3,16 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access
 $info=array();
 $qstr='';
 if($sla && $_REQUEST['a']!='add'){
-    $title='Update SLA Plan';
+    $title=__('Update SLA Plan' /* SLA is abbreviation for Service Level Agreement */);
     $action='update';
-    $submit_text='Save Changes';
+    $submit_text=__('Save Changes');
     $info=$sla->getInfo();
     $info['id']=$sla->getId();
     $qstr.='&id='.$sla->getId();
 }else {
-    $title='Add New SLA Plan';
+    $title=__('Add New SLA Plan' /* SLA is abbreviation for Service Level Agreement */);
     $action='add';
-    $submit_text='Add Plan';
+    $submit_text=__('Add Plan');
     $info['isactive']=isset($info['isactive'])?$info['isactive']:1;
     $info['enable_priority_escalation']=isset($info['enable_priority_escalation'])?$info['enable_priority_escalation']:1;
     $info['disable_overdue_alerts']=isset($info['disable_overdue_alerts'])?$info['disable_overdue_alerts']:0;
@@ -25,20 +25,20 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="do" value="<?php echo $action; ?>">
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2>Service Level Agreement</h2>
+ <h2><?php echo __('Service Level Agreement');?></h2>
  <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em>Tickets are marked overdue on grace period violation.</em>
+                <em><?php echo __('Tickets are marked overdue on grace period violation.');?></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <td width="180" class="required">
-              Name:
+              <?php echo __('Name');?>:
             </td>
             <td>
                 <input type="text" size="30" name="name" value="<?php echo $info['name']; ?>">
@@ -47,47 +47,49 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180" class="required">
-              Grace Period:
+              <?php echo __('Grace Period');?>:
             </td>
             <td>
                 <input type="text" size="10" name="grace_period" value="<?php echo $info['grace_period']; ?>">
-                <em>( in hours )</em>
+                <em>( <?php echo __('in hours');?> )</em>
                 &nbsp;<span class="error">*&nbsp;<?php echo $errors['grace_period']; ?></span>&nbsp;<i class="help-tip icon-question-sign" href="#grace_period"></i>
             </td>
         </tr>
         <tr>
             <td width="180" class="required">
-                Status:
+                <?php echo __('Status');?>:
             </td>
             <td>
-                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong>Active</strong>
-                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>>Disabled
+                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong><?php echo __('Active');?></strong>
+                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>><?php echo __('Disabled');?>
                 &nbsp;<span class="error">*&nbsp;<?php echo $errors['isactive']; ?></span>
             </td>
         </tr>
         <tr>
             <td width="180">
-                Transient:
+                <?php echo __('Transient'); ?>:
             </td>
             <td>
                 <input type="checkbox" name="transient" value="1" <?php echo $info['transient']?'checked="checked"':''; ?> >
-                SLA can be overridden on ticket transfer or help topic
-                change&nbsp;<i class="help-tip icon-question-sign" href="#transient"></i>
+                <?php echo __('SLA can be overridden on ticket transfer or help topic change'); ?>
+                &nbsp;<i class="help-tip icon-question-sign" href="#transient"></i>
             </td>
         </tr>
         <tr>
             <td width="180">
-                Ticket Overdue Alerts:
+                <?php echo __('Ticket Overdue Alerts');?>:
             </td>
             <td>
                 <input type="checkbox" name="disable_overdue_alerts" value="1" <?php echo $info['disable_overdue_alerts']?'checked="checked"':''; ?> >
-                    <strong>Disable</strong> overdue alerts notices.
-                    <em>(Override global setting)</em>
+                    <?php echo __('<strong>Disable</strong> overdue alerts notices.'); ?>
+                    <em><?php echo __('(Override global setting)'); ?></em>
             </td>
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Admin Notes</strong>: Internal notes.&nbsp;&nbsp;<i class="help-tip icon-question-sign" href="#admin_notes"></i></em>
+                <em><strong><?php echo __('Admin Notes');?></strong>: <?php echo __('Internal notes.');?>
+                &nbsp;&nbsp;<i class="help-tip icon-question-sign" href="#admin_notes"></i></em>
+                </em>
             </th>
         </tr>
         <tr>
@@ -98,9 +100,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
     </tbody>
 </table>
-<p style="padding-left:225px;">
+<p style="text-align:center;">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="slas.php"'>
+    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="slas.php"'>
 </p>
 </form>
diff --git a/include/staff/slaplans.inc.php b/include/staff/slaplans.inc.php
index ff6fa6abe5601bbbf84be2d35b8697b8c753e5f6..4acf6763ce198faa5d051e784f32aef46c7c6ecd 100644
--- a/include/staff/slaplans.inc.php
+++ b/include/staff/slaplans.inc.php
@@ -33,17 +33,19 @@ $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
 $query="$sql ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
 $res=db_query($query);
 if($res && ($num=db_num_rows($res)))
-    $showing=$pageNav->showing().' SLA plans';
+    $showing=$pageNav->showing().' '._N('SLA plan',
+        'SLA plans' /* SLA is abbreviation for Service Level Agreement */,
+        $total);
 else
-    $showing='No SLA plans found!';
+    $showing=__('No SLA plans found!' /* SLA is abbreviation for Service Level Agreement */);
 
 ?>
 
-<div style="width:700px;padding-top:5px; float:left;">
- <h2>Service Level Agreements</h2>
+<div class="pull-left" style="width:700px;padding-top:5px;">
+ <h2><?php echo __('Service Level Agreements');?></h2>
 </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
- <b><a href="slas.php?a=add" class="Icon newsla">Add New SLA Plan</a></b></div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
+ <b><a href="slas.php?a=add" class="Icon newsla"><?php echo __('Add New SLA Plan');?></a></b></div>
 <div class="clear"></div>
 <form action="slas.php" method="POST" name="slas">
  <?php csrf_token(); ?>
@@ -54,11 +56,11 @@ else
     <thead>
         <tr>
             <th width="7">&nbsp;</th>
-            <th width="320"><a <?php echo $name_sort; ?> href="slas.php?<?php echo $qstr; ?>&sort=name">Name</a></th>
-            <th width="100"><a  <?php echo $status_sort; ?> href="slas.php?<?php echo $qstr; ?>&sort=status">Status</a></th>
-            <th width="130"><a  <?php echo $period_sort; ?> href="slas.php?<?php echo $qstr; ?>&sort=period">Grace Period (hrs)</a></th>
-            <th width="120" nowrap><a  <?php echo $created_sort; ?>href="slas.php?<?php echo $qstr; ?>&sort=created">Date Added</a></th>
-            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="slas.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th>
+            <th width="320"><a <?php echo $name_sort; ?> href="slas.php?<?php echo $qstr; ?>&sort=name"><?php echo __('Name');?></a></th>
+            <th width="100"><a <?php echo $status_sort; ?> href="slas.php?<?php echo $qstr; ?>&sort=status"><?php echo __('Status');?></a></th>
+            <th width="130"><a <?php echo $period_sort; ?> href="slas.php?<?php echo $qstr; ?>&sort=period"><?php echo __('Grace Period (hrs)');?></a></th>
+            <th width="120" nowrap><a <?php echo $created_sort; ?>href="slas.php?<?php echo $qstr; ?>&sort=created"><?php echo __('Date Added');?></a></th>
+            <th width="150" nowrap><a <?php echo $updated_sort; ?>href="slas.php?<?php echo $qstr; ?>&sort=updated"><?php echo __('Last Updated');?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -84,7 +86,7 @@ else
                 <td>&nbsp;<a href="slas.php?id=<?php echo $row['id'];
                     ?>"><?php echo Format::htmlchars($row['name']);
                     ?></a>&nbsp;<?php echo $default; ?></td>
-                <td><?php echo $row['isactive']?'Active':'<b>Disabled</b>'; ?></td>
+                <td><?php echo $row['isactive']?__('Active'):'<b>'.__('Disabled').'</b>'; ?></td>
                 <td style="text-align:right;padding-right:35px;"><?php echo $row['grace_period']; ?>&nbsp;</td>
                 <td>&nbsp;<?php echo Format::db_date($row['created']); ?></td>
                 <td>&nbsp;<?php echo Format::db_datetime($row['updated']); ?></td>
@@ -96,12 +98,12 @@ else
      <tr>
         <td colspan="6">
             <?php if($res && $num){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No SLA plans found';
+                echo __('No SLA plans found' /* SLA is abbreviation for Service Level Agreement */);
             } ?>
         </td>
      </tr>
@@ -109,12 +111,12 @@ else
 </table>
 <?php
 if($res && $num): //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="enable" value="Enable" >
-    <input class="button" type="submit" name="disable" value="Disable" >
-    <input class="button" type="submit" name="delete" value="Delete" >
+    <input class="button" type="submit" name="enable" value="<?php echo __('Enable');?>" >
+    <input class="button" type="submit" name="disable" value="<?php echo __('Disable');?>" >
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete');?>" >
 </p>
 <?php
 endif;
@@ -122,26 +124,29 @@ endif;
 </form>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="enable-confirm">
-        Are you sure want to <b>enable</b> selected SLA plans?
+        <?php echo sprintf(__('Are you sure want to <b>enable</b> %s?'),
+            _N('selected SLA plan', 'selected SLA plans', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="disable-confirm">
-        Are you sure want to <b>disable</b> selected SLA plans?
+        <?php echo sprintf(__('Are you sure want to <b>disable</b> %s?'),
+            _N('selected SLA plan', 'selected SLA plans', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected SLA plans?</strong></font>
+        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
+            _N('selected SLA plan', 'selected SLA plans', 2)); ?></strong></font>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/staff.inc.php b/include/staff/staff.inc.php
index 13fd759eff5f45e6fdf023f3edca71ded1557731..1dd7d105ada2a28c76b917221460563b6604c9f7 100644
--- a/include/staff/staff.inc.php
+++ b/include/staff/staff.inc.php
@@ -5,20 +5,20 @@ $info=array();
 $qstr='';
 if($staff && $_REQUEST['a']!='add'){
     //Editing Department.
-    $title='Update Staff';
+    $title=__('Update Agent');
     $action='update';
-    $submit_text='Save Changes';
-    $passwd_text='To reset the password enter a new one below';
+    $submit_text=__('Save Changes');
+    $passwd_text=__('To reset the password enter a new one below');
     $info=$staff->getInfo();
     $info['id']=$staff->getId();
     $info['teams'] = $staff->getTeams();
     $info['signature'] = Format::viewableImages($info['signature']);
     $qstr.='&id='.$staff->getId();
 }else {
-    $title='Add New Staff';
+    $title=__('Add New Agent');
     $action='create';
-    $submit_text='Add Staff';
-    $passwd_text='Temporary password required only for "Local" authenication';
+    $submit_text=__('Add Agent');
+    $passwd_text=__('Temporary password required only for "Local" authenication');
     //Some defaults for new staff.
     $info['change_passwd']=1;
     $info['welcome_email']=1;
@@ -36,20 +36,20 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="do" value="<?php echo $action; ?>">
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2>Staff Account</h2>
+ <h2><?php echo __('Agent Account');?></h2>
  <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em><strong>User Information</strong></em>
+                <em><strong><?php echo __('User Information');?></strong></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <td width="180" class="required">
-                Username:
+                <?php echo __('Username');?>:
             </td>
             <td>
                 <input type="text" size="30" class="staff-username typeahead"
@@ -60,7 +60,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 
         <tr>
             <td width="180" class="required">
-                First Name:
+                <?php echo __('First Name');?>:
             </td>
             <td>
                 <input type="text" size="30" name="firstname" class="auto first"
@@ -70,7 +70,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180" class="required">
-                Last Name:
+                <?php echo __('Last Name');?>:
             </td>
             <td>
                 <input type="text" size="30" name="lastname" class="auto last"
@@ -80,7 +80,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180" class="required">
-                Email Address:
+                <?php echo __('Email Address');?>:
             </td>
             <td>
                 <input type="text" size="30" name="email" class="auto email"
@@ -90,19 +90,19 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-                Phone Number:
+                <?php echo __('Phone Number');?>:
             </td>
             <td>
                 <input type="text" size="18" name="phone" class="auto phone"
                     value="<?php echo $info['phone']; ?>">
                 &nbsp;<span class="error">&nbsp;<?php echo $errors['phone']; ?></span>
-                Ext <input type="text" size="5" name="phone_ext" value="<?php echo $info['phone_ext']; ?>">
+                <?php echo __('Ext');?> <input type="text" size="5" name="phone_ext" value="<?php echo $info['phone_ext']; ?>">
                 &nbsp;<span class="error">&nbsp;<?php echo $errors['phone_ext']; ?></span>
             </td>
         </tr>
         <tr>
             <td width="180">
-                Mobile Number:
+                <?php echo __('Mobile Number');?>:
             </td>
             <td>
                 <input type="text" size="18" name="mobile" class="auto mobile"
@@ -112,7 +112,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
 <?php if (!$staff) { ?>
         <tr>
-            <td width="180">Welcome Email</td>
+            <td width="180"><?php echo __('Welcome Email'); ?></td>
             <td><input type="checkbox" name="welcome_email" id="welcome-email" <?php
                 if ($info['welcome_email']) echo 'checked="checked"';
                 ?> onchange="javascript:
@@ -122,18 +122,18 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                 else if (sbk.val() == '' || sbk.val() == 'local')
                     $('#password-fields').show();
                 " />
-                 Send sign in information
+                <?php echo __('Send sign in information'); ?>
                 &nbsp;<i class="help-tip icon-question-sign" href="#welcome_email"></i>
             </td>
         </tr>
 <?php } ?>
         <tr>
             <th colspan="2">
-                <em><strong>Authentication</strong>: <?php echo $passwd_text; ?> &nbsp;<span class="error">&nbsp;<?php echo $errors['temppasswd']; ?></span>&nbsp;<i class="help-tip icon-question-sign" href="#account_password"></i></em>
+                <em><strong><?php echo __('Authentication'); ?></strong>: <?php echo $passwd_text; ?> &nbsp;<span class="error">&nbsp;<?php echo $errors['temppasswd']; ?></span>&nbsp;<i class="help-tip icon-question-sign" href="#account_password"></i></em>
             </th>
         </tr>
         <tr>
-            <td>Authentication Backend</td>
+            <td><?php echo __('Authentication Backend'); ?></td>
             <td>
             <select name="backend" id="backend-selection" onchange="javascript:
                 if (this.value != '' && this.value != 'local')
@@ -141,13 +141,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
                 else if (!$('#welcome-email').is(':checked'))
                     $('#password-fields').show();
                 ">
-                <option value="">&mdash; Use any available backend &mdash;</option>
+                <option value="">&mdash; <?php echo __('Use any available backend'); ?> &mdash;</option>
             <?php foreach (StaffAuthenticationBackend::allRegistered() as $ab) {
                 if (!$ab->supportsInteractiveAuthentication()) continue; ?>
                 <option value="<?php echo $ab::$id; ?>" <?php
                     if ($info['backend'] == $ab::$id)
                         echo 'selected="selected"'; ?>><?php
-                    echo $ab::$name; ?></option>
+                    echo $ab->getName(); ?></option>
             <?php } ?>
             </select>
             </td>
@@ -158,7 +158,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             echo 'display:none;'; ?>">
         <tr>
             <td width="180">
-                Password:
+                <?php echo __('Password');?>:
             </td>
             <td>
                 <input type="password" size="18" name="passwd1" value="<?php echo $info['passwd1']; ?>">
@@ -167,7 +167,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-                Confirm Password:
+                <?php echo __('Confirm Password');?>:
             </td>
             <td>
                 <input type="password" size="18" name="passwd2" value="<?php echo $info['passwd2']; ?>">
@@ -177,66 +177,70 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 
         <tr>
             <td width="180">
-                Forced Password Change:
+                <?php echo __('Forced Password Change');?>:
             </td>
             <td>
                 <input type="checkbox" name="change_passwd" value="0" <?php echo $info['change_passwd']?'checked="checked"':''; ?>>
-                <strong>Force</strong> password change on next login.&nbsp;<i class="help-tip icon-question-sign" href="#forced_password_change"></i>
+                <?php echo __('<strong>Force</strong> password change on next login.');?>
+                &nbsp;<i class="help-tip icon-question-sign" href="#forced_password_change"></i>
             </td>
         </tr>
     </tbody>
     <tbody>
         <tr>
             <th colspan="2">
-                <em><strong>Staff's Signature</strong>: Optional signature used on outgoing emails. &nbsp;<span class="error">&nbsp;<?php echo $errors['signature']; ?></span>&nbsp;<i class="help-tip icon-question-sign" href="#agents_signature"></i></em>
+                <em><strong><?php echo __("Agent's Signature");?></strong>:
+                <?php echo __('Optional signature used on outgoing emails.');?>
+                &nbsp;<span class="error">&nbsp;<?php echo $errors['signature']; ?></span></em>
+                &nbsp;<i class="help-tip icon-question-sign" href="#agents_signature"></i></em>
             </th>
         </tr>
         <tr>
             <td colspan=2>
                 <textarea class="richtext no-bar" name="signature" cols="21"
                     rows="5" style="width: 60%;"><?php echo $info['signature']; ?></textarea>
-                <br><em>Signature is made available as a choice, on ticket reply.</em>
+                <br><em><?php echo __('Signature is made available as a choice, on ticket reply.');?></em>
             </td>
         </tr>
         <tr>
             <th colspan="2">
-                <em><strong>Account Status & Settings</strong>: Dept. and assigned group controls access permissions.</em>
+                <em><strong><?php echo __('Account Status & Settings');?></strong>: <?php echo __('Department and group assigned control access permissions.');?></em>
             </th>
         </tr>
         <tr>
             <td width="180" class="required">
-                Account Type:
+                <?php echo __('Account Type');?>:
             </td>
             <td>
                 <input type="radio" name="isadmin" value="1" <?php echo $info['isadmin']?'checked="checked"':''; ?>>
-                    <font color="red"><strong>Admin</strong></font>
-                <input type="radio" name="isadmin" value="0" <?php echo !$info['isadmin']?'checked="checked"':''; ?>><strong>Staff</strong>
+                    <font color="red"><strong><?php echo __('Admin');?></strong></font>
+                <input type="radio" name="isadmin" value="0" <?php echo !$info['isadmin']?'checked="checked"':''; ?>><strong><?php echo __('Agent');?></strong>
                 &nbsp;<span class="error">&nbsp;<?php echo $errors['isadmin']; ?></span>
             </td>
         </tr>
         <tr>
             <td width="180" class="required">
-                Account Status:
+                <?php echo __('Account Status');?>:
             </td>
             <td>
-                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong>Active</strong>
-                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>><strong>Locked</strong>
+                <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong><?php echo __('Active');?></strong>
+                <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>><strong><?php echo __('Locked');?></strong>
                 &nbsp;<span class="error">&nbsp;<?php echo $errors['isactive']; ?></span>&nbsp;<i class="help-tip icon-question-sign" href="#account_status"></i>
             </td>
         </tr>
         <tr>
             <td width="180" class="required">
-                Assigned Group:
+                <?php echo __('Assigned Group');?>:
             </td>
             <td>
                 <select name="group_id" id="group_id">
-                    <option value="0">&mdash; Select Group &mdash;</option>
+                    <option value="0">&mdash; <?php echo __('Select Group');?> &mdash;</option>
                     <?php
                     $sql='SELECT group_id, group_name, group_enabled as isactive FROM '.GROUP_TABLE.' ORDER BY group_name';
                     if(($res=db_query($sql)) && db_num_rows($res)){
                         while(list($id,$name,$isactive)=db_fetch_row($res)){
                             $sel=($info['group_id']==$id)?'selected="selected"':'';
-                            echo sprintf('<option value="%d" %s>%s %s</option>',$id,$sel,$name,($isactive?'':' (Disabled)'));
+                            echo sprintf('<option value="%d" %s>%s %s</option>',$id,$sel,$name,($isactive?'':__('(disabled)')));
                         }
                     }
                     ?>
@@ -246,11 +250,11 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180" class="required">
-                Primary Department:
+                <?php echo __('Primary Department');?>:
             </td>
             <td>
                 <select name="dept_id" id="dept_id">
-                    <option value="0">&mdash; Select Department &mdash;</option>
+                    <option value="0">&mdash; <?php echo __('Select Department');?> &mdash;</option>
                     <?php
                     $sql='SELECT dept_id, dept_name FROM '.DEPT_TABLE.' ORDER BY dept_name';
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -266,11 +270,11 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180" class="required">
-                Staff's Time Zone:
+                <?php echo __("Agent's Time Zone");?>:
             </td>
             <td>
                 <select name="timezone_id" id="timezone_id">
-                    <option value="0">&mdash; Select Time Zone &mdash;</option>
+                    <option value="0">&mdash; <?php echo __('Select Time Zone');?> &mdash;</option>
                     <?php
                     $sql='SELECT id, offset,timezone FROM '.TIMEZONE_TABLE.' ORDER BY id';
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -286,37 +290,45 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-               Daylight Saving:
+               <?php echo __('Daylight Saving');?>:
             </td>
             <td>
                 <input type="checkbox" name="daylight_saving" value="1" <?php echo $info['daylight_saving']?'checked="checked"':''; ?>>
-                Observe daylight saving
-                <em>(Current Time: <strong><?php echo Format::date($cfg->getDateTimeFormat(),Misc::gmtime(),$info['tz_offset'],$info['daylight_saving']); ?></strong>)&nbsp;<i class="help-tip icon-question-sign" href="#daylight_saving"></i></em>
+                <?php echo __('Observe daylight saving');?>
+                <em>(<?php echo __('Current Time');?>: <strong><?php
+                    echo Format::date($cfg->getDateTimeFormat(),Misc::gmtime(),$info['tz_offset'],$info['daylight_saving']);
+                ?></strong>)
+                &nbsp;<i class="help-tip icon-question-sign" href="#daylight_saving"></i>
+                </em>
             </td>
         </tr>
         <tr>
             <td width="180">
-                Limited Access:
+                <?php echo __('Limited Access');?>:
             </td>
             <td>
-                <input type="checkbox" name="assigned_only" value="1" <?php echo $info['assigned_only']?'checked="checked"':''; ?>>Limit ticket access to ONLY assigned tickets.&nbsp;<i class="help-tip icon-question-sign" href="#limited_access"></i>
+                <input type="checkbox" name="assigned_only" value="1" <?php echo $info['assigned_only']?'checked="checked"':''; ?>><?php echo __('Limit ticket access to ONLY assigned tickets.');?>
+                &nbsp;<i class="help-tip icon-question-sign" href="#limited_access"></i>
             </td>
         </tr>
         <tr>
             <td width="180">
-                Directory Listing:
+                <?php echo __('Directory Listing');?>:
             </td>
             <td>
-                <input type="checkbox" name="isvisible" value="1" <?php echo $info['isvisible']?'checked="checked"':''; ?>>&nbsp;Make Visible in the Staff Directory&nbsp;<i class="help-tip icon-question-sign" href="#directory_listing"></i>
+                <input type="checkbox" name="isvisible" value="1" <?php echo $info['isvisible']?'checked="checked"':''; ?>>&nbsp;<?php
+                echo __('Make visible in the Agent Directory'); ?>
+                &nbsp;<i class="help-tip icon-question-sign" href="#directory_listing"></i>
             </td>
         </tr>
         <tr>
             <td width="180">
-                Vacation Mode:
+                <?php echo __('Vacation Mode');?>:
             </td>
             <td>
                 <input type="checkbox" name="onvacation" value="1" <?php echo $info['onvacation']?'checked="checked"':''; ?>>
-                    Change Status to Vacation Mode&nbsp;<i class="help-tip icon-question-sign" href="#vacation_mode"></i>
+                    <?php echo __('Change Status to Vacation Mode'); ?>
+                    &nbsp;<i class="help-tip icon-question-sign" href="#vacation_mode"></i>
             </td>
         </tr>
         <?php
@@ -325,19 +337,19 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
          if(($res=db_query($sql)) && db_num_rows($res)){ ?>
         <tr>
             <th colspan="2">
-                <em><strong>Assigned Teams</strong>: Staff will have access to tickets assigned to a team they belong to regardless of the ticket's department. </em>
+                <em><strong><?php echo __('Assigned Teams');?></strong>: <?php echo __("Agent will have access to tickets assigned to a team they belong to regardless of the ticket's department.");?> </em>
             </th>
         </tr>
         <?php
          while(list($id,$name,$isactive)=db_fetch_row($res)){
              $checked=($info['teams'] && in_array($id,$info['teams']))?'checked="checked"':'';
              echo sprintf('<tr><td colspan=2><input type="checkbox" name="teams[]" value="%d" %s>%s %s</td></tr>',
-                     $id,$checked,$name,($isactive?'':' (Disabled)'));
+                     $id,$checked,$name,($isactive?'':__('(disabled)')));
          }
         } ?>
         <tr>
             <th colspan="2">
-                <em><strong>Admin Notes</strong></em>
+                <em><strong><?php echo __('Internal Notes'); ?></strong></em>
             </th>
         </tr>
         <tr>
@@ -350,7 +362,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 </table>
 <p style="padding-left:250px;">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="staff.php"'>
+    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="staff.php"'>
 </p>
 </form>
diff --git a/include/staff/staffmembers.inc.php b/include/staff/staffmembers.inc.php
index 1ff5fde726c7813e3475ea33f6453d939cb489c1..f440ea3965badfe2218106765adfa496e6dfc514 100644
--- a/include/staff/staffmembers.inc.php
+++ b/include/staff/staffmembers.inc.php
@@ -54,12 +54,12 @@ $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
 $query="$select $from $where GROUP BY staff.staff_id ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
 //echo $query;
 ?>
-<h2>Staff Members</h2>
-<div style="width:700px; float:left;">
+<h2><?php echo __('Agents');?></h2>
+<div class="pull-left" style="width:700px;">
     <form action="staff.php" method="GET" name="filter">
      <input type="hidden" name="a" value="filter" >
         <select name="did" id="did">
-             <option value="0">&mdash; All Departments &mdash;</option>
+             <option value="0">&mdash; <?php echo __('All Department');?> &mdash;</option>
              <?php
              $sql='SELECT dept.dept_id, dept.dept_name,count(staff.staff_id) as users  '.
                   'FROM '.DEPT_TABLE.' dept '.
@@ -74,7 +74,7 @@ $query="$select $from $where GROUP BY staff.staff_id ORDER BY $order_by LIMIT ".
              ?>
         </select>
         <select name="gid" id="gid">
-            <option value="0">&mdash; All Groups &mdash;</option>
+            <option value="0">&mdash; <?php echo __('All Groups');?> &mdash;</option>
              <?php
              $sql='SELECT grp.group_id, group_name,count(staff.staff_id) as users '.
                   'FROM '.GROUP_TABLE.' grp '.
@@ -89,7 +89,7 @@ $query="$select $from $where GROUP BY staff.staff_id ORDER BY $order_by LIMIT ".
              ?>
         </select>
         <select name="tid" id="tid">
-            <option value="0">&mdash; All Teams &mdash;</option>
+            <option value="0">&mdash; <?php echo __('All Teams');?> &mdash;</option>
              <?php
              $sql='SELECT team.team_id, team.name, count(member.staff_id) as users FROM '.TEAM_TABLE.' team '.
                   'INNER JOIN '.TEAM_MEMBER_TABLE.' member ON(member.team_id=team.team_id) '.
@@ -103,17 +103,17 @@ $query="$select $from $where GROUP BY staff.staff_id ORDER BY $order_by LIMIT ".
              ?>
         </select>
         &nbsp;&nbsp;
-        <input type="submit" name="submit" value="Apply"/>
+        <input type="submit" name="submit" value="<?php echo __('Apply');?>"/>
     </form>
  </div>
-<div style="float:right;text-align:right;padding-right:5px;"><b><a href="staff.php?a=add" class="Icon newstaff">Add New Staff</a></b></div>
+<div class="pull-right flush-right" style="padding-right:5px;"><b><a href="staff.php?a=add" class="Icon newstaff"><?php echo __('Add New Agent');?></a></b></div>
 <div class="clear"></div>
 <?php
 $res=db_query($query);
-if($res && ($num=db_num_rows($res)))        
-    $showing=$pageNav->showing();
+if($res && ($num=db_num_rows($res)))
+    $showing=$pageNav->showing() . ' ' . _N('agent', 'agents', $num);
 else
-    $showing='No staff found!';
+    $showing=__('No agents found!');
 ?>
 <form action="staff.php" method="POST" name="staff" >
  <?php csrf_token(); ?>
@@ -123,14 +123,14 @@ else
     <caption><?php echo $showing; ?></caption>
     <thead>
         <tr>
-            <th width="7px">&nbsp;</th>        
-            <th width="200"><a <?php echo $name_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=name">Name</a></th>
-            <th width="100"><a <?php echo $username_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=username">UserName</a></th>
-            <th width="100"><a  <?php echo $status_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=status">Status</a></th>
-            <th width="120"><a  <?php echo $group_sort; ?>href="staff.php?<?php echo $qstr; ?>&sort=group">Group</a></th>
-            <th width="150"><a  <?php echo $dept_sort; ?>href="staff.php?<?php echo $qstr; ?>&sort=dept">Department</a></th>
-            <th width="100"><a <?php echo $created_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=created">Created</a></th>
-            <th width="145"><a <?php echo $login_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=login">Last Login</a></th>
+            <th width="7px">&nbsp;</th>
+            <th width="200"><a <?php echo $name_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=name"><?php echo __('Name');?></a></th>
+            <th width="100"><a <?php echo $username_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=username"><?php echo __('Username');?></a></th>
+            <th width="100"><a  <?php echo $status_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=status"><?php echo __('Status');?></a></th>
+            <th width="120"><a  <?php echo $group_sort; ?>href="staff.php?<?php echo $qstr; ?>&sort=group"><?php echo __('Group');?></a></th>
+            <th width="150"><a  <?php echo $dept_sort; ?>href="staff.php?<?php echo $qstr; ?>&sort=dept"><?php echo __('Department');?></a></th>
+            <th width="100"><a <?php echo $created_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=created"><?php echo __('Created');?></a></th>
+            <th width="145"><a <?php echo $login_sort; ?> href="staff.php?<?php echo $qstr; ?>&sort=login"><?php echo __('Last Login');?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -147,7 +147,7 @@ else
                   <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['staff_id']; ?>" <?php echo $sel?'checked="checked"':''; ?> >
                 <td><a href="staff.php?id=<?php echo $row['staff_id']; ?>"><?php echo Format::htmlchars($row['name']); ?></a>&nbsp;</td>
                 <td><?php echo $row['username']; ?></td>
-                <td><?php echo $row['isactive']?'Active':'<b>Locked</b>'; ?>&nbsp;<?php echo $row['onvacation']?'<small>(<i>vacation</i>)</small>':''; ?></td>
+                <td><?php echo $row['isactive']?__('Active'):'<b>'.__('Locked').'</b>'; ?>&nbsp;<?php echo $row['onvacation']?'<small>(<i>'.__('vacation').'</i>)</small>':''; ?></td>
                 <td><a href="groups.php?id=<?php echo $row['group_id']; ?>"><?php echo Format::htmlchars($row['group_name']); ?></a></td>
                 <td><a href="departments.php?id=<?php echo $row['dept_id']; ?>"><?php echo Format::htmlchars($row['dept']); ?></a></td>
                 <td><?php echo Format::db_date($row['created']); ?></td>
@@ -160,12 +160,12 @@ else
      <tr>
         <td colspan="8">
             <?php if($res && $num){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No staff members found!';
+                echo __('No agents found!');
             } ?>
         </td>
      </tr>
@@ -173,14 +173,14 @@ else
 </table>
 <?php
 if($res && $num): //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="enable" value="Enable" >
+    <input class="button" type="submit" name="enable" value="<?php echo __('Enable');?>" >
     &nbsp;&nbsp;
-    <input class="button" type="submit" name="disable" value="Lock" >
+    <input class="button" type="submit" name="disable" value="<?php echo __('Lock');?>" >
     &nbsp;&nbsp;
-    <input class="button" type="submit" name="delete" value="Delete">
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete');?>">
 </p>
 <?php
 endif;
@@ -188,28 +188,31 @@ endif;
 </form>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="enable-confirm">
-        Are you sure want to <b>enable</b> (unlock) selected staff?
+        <?php echo sprintf(__('Are you sure want to <b>enable</b> (unlock) %s?'),
+            _N('selected agent', 'selected agents', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="disable-confirm">
-        Are you sure want to <b>disable</b> (lock) selected staff?
-        <br><br>Locked staff won't be able to login to Staff Control Panel.
+        <?php echo sprintf(__('Are you sure want to <b>disable</b> (lock) %s?'),
+            _N('selected agent', 'selected agents', 2));?>
+        <br><br><?php echo __("Locked staff won't be able to login to Staff Control Panel.");?>
     </p>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected staff?</strong></font>
-        <br><br>Deleted staff CANNOT be recovered.
+        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
+            _N('selected agent', 'selected agents', 2));?></strong></font>
+        <br><br><?php echo __('Deleted data CANNOT be recovered.');?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/syslogs.inc.php b/include/staff/syslogs.inc.php
index 13cb38cf8e52f97978f7cb5dbaf8446a36916231..b264ffa05386b762926f28c26270f1634c580ae8 100644
--- a/include/staff/syslogs.inc.php
+++ b/include/staff/syslogs.inc.php
@@ -8,20 +8,20 @@ if($_REQUEST['type']) {
 $type=null;
 switch(strtolower($_REQUEST['type'])){
     case 'error':
-        $title='Errors';
+        $title=__('Errors');
         $type=$_REQUEST['type'];
         break;
     case 'warning':
-        $title='Warnings';
+        $title=__('Warnings');
         $type=$_REQUEST['type'];
         break;
     case 'debug':
-        $title='Debug logs';
+        $title=__('Debug logs');
         $type=$_REQUEST['type'];
         break;
     default:
         $type=null;
-        $title='All logs';
+        $title=__('All logs');
 }
 
 $qwhere =' WHERE 1';
@@ -33,7 +33,7 @@ if($type)
 $startTime  =($_REQUEST['startDate'] && (strlen($_REQUEST['startDate'])>=8))?strtotime($_REQUEST['startDate']):0;
 $endTime    =($_REQUEST['endDate'] && (strlen($_REQUEST['endDate'])>=8))?strtotime($_REQUEST['endDate']):0;
 if( ($startTime && $startTime>time()) or ($startTime>$endTime && $endTime>0)){
-    $errors['err']='Entered date span is invalid. Selection ignored.';
+    $errors['err']=__('Entered date span is invalid. Selection ignored.');
     $startTime=$endTime=0;
 }else{
     if($startTime){
@@ -80,27 +80,29 @@ $res=db_query($query);
 if($res && ($num=db_num_rows($res)))
     $showing=$pageNav->showing().' '.$title;
 else
-    $showing='No logs found!';
+    $showing=__('No logs found!');
 ?>
 
-<h2>System Logs&nbsp;<i class="help-tip icon-question-sign" href="#system_logs"></i></h2>
+<h2><?php echo __('System Logs');?>
+    &nbsp;<i class="help-tip icon-question-sign" href="#system_logs"></i>
+</h2>
 <div id='filter' >
  <form action="logs.php" method="get">
     <div style="padding-left:2px;">
-        <b>Date Span</b>&nbsp;<i class="help-tip icon-question-sign" href="#date_span"></i>
-        &nbsp;From&nbsp;<input class="dp" id="sd" size=15 name="startDate" value="<?php echo Format::htmlchars($_REQUEST['startDate']); ?>" autocomplete=OFF>
-            &nbsp;&nbsp; to &nbsp;&nbsp;
-            <input class="dp" id="ed" size=15 name="endDate" value="<?php echo Format::htmlchars($_REQUEST['endDate']); ?>" autocomplete=OFF>
-            &nbsp;&nbsp;
-            &nbsp;Type&nbsp;<i class="help-tip icon-question-sign" href="#type"></i>
+        <b><?php echo __('Date Span'); ?></b>&nbsp;<i class="help-tip icon-question-sign" href="#date_span"></i>
+        <?php echo __('Between'); ?>:
+        <input class="dp" id="sd" size=15 name="startDate" value="<?php echo Format::htmlchars($_REQUEST['startDate']); ?>" autocomplete=OFF>
+        &nbsp;&nbsp;
+        <input class="dp" id="ed" size=15 name="endDate" value="<?php echo Format::htmlchars($_REQUEST['endDate']); ?>" autocomplete=OFF>
+        &nbsp;<?php echo __('Log Level'); ?>:&nbsp;<i class="help-tip icon-question-sign" href="#type"></i>
             <select name='type'>
-                <option value="" selected>All</option>
-                <option value="Error" <?php echo ($type=='Error')?'selected="selected"':''; ?>>Errors</option>
-                <option value="Warning" <?php echo ($type=='Warning')?'selected="selected"':''; ?>>Warnings</option>
-                <option value="Debug" <?php echo ($type=='Debug')?'selected="selected"':''; ?>>Debug</option>
+                <option value="" selected><?php echo __('All');?></option>
+                <option value="Error" <?php echo ($type=='Error')?'selected="selected"':''; ?>><?php echo __('Errors');?></option>
+                <option value="Warning" <?php echo ($type=='Warning')?'selected="selected"':''; ?>><?php echo __('Warnings');?></option>
+                <option value="Debug" <?php echo ($type=='Debug')?'selected="selected"':''; ?>><?php echo __('Debug');?></option>
             </select>
             &nbsp;&nbsp;
-            <input type="submit" Value="Go!" />
+            <input type="submit" Value="<?php echo __('Go!');?>" />
     </div>
  </form>
 </div>
@@ -112,11 +114,11 @@ else
     <caption><?php echo $showing; ?></caption>
     <thead>
         <tr>
-            <th width="7">&nbsp;</th>        
-            <th width="320"><a <?php echo $title_sort; ?> href="logs.php?<?php echo $qstr; ?>&sort=title">Log Title</a></th>
-            <th width="100"><a  <?php echo $type_sort; ?> href="logs.php?<?php echo $qstr; ?>&sort=type">Log Type</a></th>
-            <th width="200" nowrap><a  <?php echo $date_sort; ?>href="logs.php?<?php echo $qstr; ?>&sort=date">Log Date</a></th>
-            <th width="120"><a  <?php echo $ip_sort; ?> href="logs.php?<?php echo $qstr; ?>&sort=ip">IP Address</a></th>
+            <th width="7">&nbsp;</th>
+            <th width="320"><a <?php echo $title_sort; ?> href="logs.php?<?php echo $qstr; ?>&sort=title"><?php echo __('Log Title');?></a></th>
+            <th width="100"><a  <?php echo $type_sort; ?> href="logs.php?<?php echo $qstr; ?>&sort=type"><?php echo __('Log Type');?></a></th>
+            <th width="200" nowrap><a  <?php echo $date_sort; ?>href="logs.php?<?php echo $qstr; ?>&sort=date"><?php echo __('Log Date');?></a></th>
+            <th width="120"><a  <?php echo $ip_sort; ?> href="logs.php?<?php echo $qstr; ?>&sort=ip"><?php echo __('IP Address');?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -131,7 +133,7 @@ else
                 ?>
             <tr id="<?php echo $row['log_id']; ?>">
                 <td width=7px>
-                  <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['log_id']; ?>" 
+                  <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['log_id']; ?>"
                             <?php echo $sel?'checked="checked"':''; ?>> </td>
                 <td>&nbsp;<a class="tip" href="#log/<?php echo $row['log_id']; ?>"><?php echo Format::htmlchars($row['title']); ?></a></td>
                 <td><?php echo $row['log_type']; ?></td>
@@ -146,12 +148,12 @@ else
      <tr>
         <td colspan="6">
             <?php if($res && $num){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No logs found';
+                echo __('No logs found');
             } ?>
         </td>
      </tr>
@@ -159,10 +161,10 @@ else
 </table>
 <?php
 if($res && $num): //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="delete" value="Delete Selected Entries">
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete Selected Entries');?>">
 </p>
 <?php
 endif;
@@ -170,21 +172,22 @@ endif;
 </form>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected logs?</strong></font>
-        <br><br>Deleted logs CANNOT be recovered.
+        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
+            _N('selected log entry', 'selected log entries', 2));?></strong></font>
+        <br><br><?php echo __('Deleted data CANNOT be recovered.');?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/system.inc.php b/include/staff/system.inc.php
index d9f3b6275743667e9ea84ac7473c14ce1fab1f52..4238e3dbeced405a5662c96a01598aa9764086e6 100644
--- a/include/staff/system.inc.php
+++ b/include/staff/system.inc.php
@@ -2,87 +2,138 @@
 if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied');
 
 $commit = GIT_VERSION != '$git' ? GIT_VERSION : (
-    @shell_exec('git rev-parse HEAD') ?: '?');
+    @shell_exec('git rev-parse HEAD | cut -b 1-8') ?: '?');
+
+$extensions = array(
+        'gd' => array(
+            'name' => 'gdlib',
+            'desc' => __('Used for image manipulation and PDF printing')
+            ),
+        'imap' => array(
+            'name' => 'imap',
+            'desc' => __('Used for email fetching')
+            ),
+        'xml' => array(
+            'name' => 'xml',
+            'desc' => __('XML API')
+            ),
+        'dom' => array(
+            'name' => 'xml-dom',
+            'desc' => __('Used for HTML email processing')
+            ),
+        'json' => array(
+            'name' => 'json',
+            'desc' => __('Improves performance creating and processing JSON')
+            ),
+        'mbstring' => array(
+            'name' => 'mbstring',
+            'desc' => __('Highly recommended for non western european language content')
+            ),
+        'phar' => array(
+            'name' => 'phar',
+            'desc' => __('Highly recommended for plugins and language packs')
+            ),
+        );
 
 ?>
-<h2>About this osTicket Installation</h2>
+<h2><?php echo __('About this osTicket Installation'); ?></h2>
 <br/>
 <table class="list" width="100%";>
 <thead>
-    <tr><th colspan="2">Server Information</th></tr>
+    <tr><th colspan="2"><?php echo __('Server Information'); ?></th></tr>
 </thead>
 <tbody>
-    <tr><td>osTicket Version</td>
-        <td><?php echo sprintf("%s (%s)", THIS_VERSION, $commit); ?></td></tr>
-    <tr><td>Server Software</td>
-        <td><?php echo $_SERVER['SERVER_SOFTWARE']; ?></td></tr>
-    <tr><td>PHP Version</td>
-        <td><?php echo phpversion(); ?></td></tr>
-    <tr><td>MySQL Version</td>
-        <td><?php echo db_version(); ?></td></tr>
-
-    <tr><td>PHP Extensions</td>
-        <td><table><tbody>
-            <tr><td><i class="icon icon-<?php
-                    echo extension_loaded('gd')?'check':'warning-sign'; ?>"></i></td>
-                <td>gdlib</td>
-                <td>Used for image manipulation and PDF printing</td></tr>
-            <tr><td><i class="icon icon-<?php
-                    echo extension_loaded('imap')?'check':'warning-sign'; ?>"></i></td>
-                <td>imap</td>
-                <td>Used for email fetching</td></tr>
-            <tr><td><i class="icon icon-<?php
-                    echo extension_loaded('xml')?'check':'warning-sign'; ?>"></i></td>
-                <td>xml</td>
-                <td>XML API</td></tr>
-            <tr><td><i class="icon icon-<?php
-                    echo extension_loaded('dom')?'check':'warning-sign'; ?>"></i></td>
-                <td>xml-dom</td>
-                <td>Used for HTML email processing</td></tr>
-            <tr><td><i class="icon icon-<?php
-                    echo extension_loaded('json')?'check':'warning-sign'; ?>"></i></td>
-                <td>json</td>
-                <td>Improves performance creating and processing JSON</td></tr>
-            <tr><td><i class="icon icon-<?php
-                    echo extension_loaded('gettext')?'check':'warning-sign'; ?>"></i></td>
-                <td>gettext</td>
-                <td>Improves performance for non US-English configurations</td></tr>
-            <tr><td><i class="icon icon-<?php
-                    echo extension_loaded('mbstring')?'check':'warning-sign'; ?>"></i></td>
-                <td>mbstring</td>
-                <td>Highly recommended for non western european language content</td></tr>
-            <tr><td><i class="icon icon-<?php
-                    echo extension_loaded('phar')?'check':'warning-sign'; ?>"></i></td>
-                <td>phar</td>
-                <td>Highly recommended for plugins and language packs</td></tr>
-        </tbody></table></td></tr>
-    <tr><td>PHP Settings</td>
-        <td><table><tbody>
-        <tr><td><i class="icon icon-<?php
-                echo ini_get('cgi.fix_pathinfo') == 1 ?'check':'warning-sign'; ?>"></i>
-            </td><td>
-            <code>cgi.fix_pathinfo</code> =
-                <?php echo ini_get('cgi.fix_pathinfo'); ?>
-            </td><td>
-            <span class="faded">"1" is recommended if AJAX is not working</span>
-        </td></tr>
-        </tbody></table></td></tr>
+    <tr><td><?php echo __('osTicket Version'); ?></td>
+        <td><span class="ltr"><?php
+        echo sprintf("%s (%s)", THIS_VERSION, trim($commit)); ?></span></td></tr>
+    <tr><td><?php echo __('Web Server Software'); ?></td>
+        <td><span class="ltr"><?php echo $_SERVER['SERVER_SOFTWARE']; ?></span></td></tr>
+    <tr><td><?php echo __('MySQL Version'); ?></td>
+        <td><span class="ltr"><?php echo db_version(); ?></span></td></tr>
+    <tr><td><?php echo __('PHP Version'); ?></td>
+        <td><span class="ltr"><?php echo phpversion(); ?></span></td></tr>
 </tbody>
 <thead>
-    <tr><th colspan="2">Database Usage</th></tr>
+    <tr><th colspan="2"><?php echo __('PHP Extensions'); ?></th></tr>
 </thead>
 <tbody>
-    <tr><td>Database Space Used</td>
+    <?php
+    foreach($extensions as $ext => $info) { ?>
+    <tr><td><?php echo $info['name']; ?></td>
+        <td><?php
+            echo sprintf('<i class="icon icon-%s"></i> %s',
+                    extension_loaded($ext) ? 'check' : 'warning-sign',
+                    $info['desc']);
+            ?>
+        </td>
+    </tr>
+    <?php
+    } ?>
+</tbody>
+<thead>
+    <tr><th colspan="2"><?php echo __('PHP Settings'); ?></th></tr>
+</thead>
+<tbody>
+    <tr>
+        <td><span class="ltr"><code>cgi.fix_pathinfo</code></span></td>
+        <td><i class="icon icon-<?php
+                echo ini_get('cgi.fix_pathinfo') == 1 ? 'check' : 'warning-sign'; ?>"></i>
+                <span class="faded"><?php echo __('"1" is recommended if AJAX is not working'); ?></span>
+        </td>
+    </tr>
+    <tr>
+        <td><span class="ltr"><code>date.timezone</code></span></td>
+        <td><i class="icon icon-<?php
+                echo ini_get('date.timezone') ? 'check' : 'warning-sign'; ?>"></i>
+                <span class="faded"><?php
+                    echo ini_get('date.timezone')
+                    ?: __('Setting default timezone is highly recommended');
+                    ?></span>
+        </td>
+    </tr>
+</tbody>
+<thead>
+    <tr><th colspan="2"><?php echo __('Database Information and Usage'); ?></th></tr>
+</thead>
+<tbody>
+    <tr><td><?php echo __('Schema'); ?></td>
+        <td><?php echo sprintf('<span class="ltr">%s (%s)</span>', DBNAME, DBHOST); ?> </td>
+    </tr>
+    <tr><td><?php echo __('Schema Signature'); ?></td>
+        <td><?php echo $cfg->getSchemaSignature(); ?> </td>
+    </tr>
+    <tr><td><?php echo __('Space Used'); ?></td>
         <td><?php
         $sql = 'SELECT sum( data_length + index_length ) / 1048576 total_size
             FROM information_schema.TABLES WHERE table_schema = '
             .db_input(DBNAME);
         $space = db_result(db_query($sql));
         echo sprintf('%.2f MiB', $space); ?></td>
-    <tr><td>Database Space for Attachments</td>
+    <tr><td><?php echo __('Space for Attachments'); ?></td>
         <td><?php
         $sql = 'SELECT SUM(LENGTH(filedata)) / 1048576 FROM '.FILE_CHUNK_TABLE;
         $space = db_result(db_query($sql));
         echo sprintf('%.2f MiB', $space); ?></td>
 </tbody>
 </table>
+<br/>
+<h2><?php echo __('Installed Language Packs'); ?></h2>
+<div style="margin: 0 20px">
+<?php
+    foreach (Internationalization::availableLanguages() as $info) {
+        $p = $info['path'];
+        if ($info['phar']) $p = 'phar://' . $p;
+        if (file_exists($p . '/MANIFEST.php')) {
+            $manifest = (include $p . '/MANIFEST.php'); ?>
+    <h3><strong><?php echo Internationalization::getLanguageDescription($info['code']); ?></strong>
+        &mdash; <?php echo $manifest['Language']; ?>
+<?php       if ($info['phar'])
+                Plugin::showVerificationBadge($info['path']);
+            ?>
+        </h3>
+        <div><?php echo __('Version'); ?>: <?php echo $manifest['Version']; ?>,
+            <?php echo __('Built'); ?>: <?php echo $manifest['Build-Date']; ?>
+        </div>
+<?php }
+    } ?>
+</div>
diff --git a/include/staff/team.inc.php b/include/staff/team.inc.php
index 994abb5a29f0a2a26d4fa1dcae054ea6cad7108d..4b2feacbfd06c1898990ce90cfc9ce13f5b05321 100644
--- a/include/staff/team.inc.php
+++ b/include/staff/team.inc.php
@@ -4,16 +4,16 @@ $info=array();
 $qstr='';
 if($team && $_REQUEST['a']!='add'){
     //Editing Team
-    $title='Update Team';
+    $title=__('Update Team');
     $action='update';
-    $submit_text='Save Changes';
+    $submit_text=__('Save Changes');
     $info=$team->getInfo();
     $info['id']=$team->getId();
     $qstr.='&id='.$team->getId();
 }else {
-    $title='Add New Team';
+    $title=__('Add New Team');
     $action='create';
-    $submit_text='Create Team';
+    $submit_text=__('Create Team');
     $info['isenabled']=1;
     $info['noalerts']=0;
     $qstr.='&a='.$_REQUEST['a'];
@@ -25,7 +25,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="do" value="<?php echo $action; ?>">
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="id" value="<?php echo $info['id']; ?>">
- <h2>Team
+ <h2><?php echo __('Team');?></h2>
     <i class="help-tip icon-question-sign" href="#teams"></i>
     </h2>
  <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
@@ -33,14 +33,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em><strong>Team Information</strong>:</em>
+                <em><strong><?php echo __('Team Information'); ?></strong>:</em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <td width="180" class="required">
-                Name:
+                <?php echo __('Name');?>:
             </td>
             <td>
                 <input type="text" size="30" name="name" value="<?php echo $info['name']; ?>">
@@ -49,13 +49,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180" class="required">
-                Status:
+                <?php echo __('Status');?>:
             </td>
             <td>
                 <span>
-                <input type="radio" name="isenabled" value="1" <?php echo $info['isenabled']?'checked="checked"':''; ?>><strong>Active</strong>
+                <input type="radio" name="isenabled" value="1" <?php echo $info['isenabled']?'checked="checked"':''; ?>><strong><?php echo __('Active');?></strong>
                 &nbsp;
-                <input type="radio" name="isenabled" value="0" <?php echo !$info['isenabled']?'checked="checked"':''; ?>>Disabled
+                <input type="radio" name="isenabled" value="0" <?php echo !$info['isenabled']?'checked="checked"':''; ?>><?php echo __('Disabled');?>
                 &nbsp;<span class="error">*&nbsp;</span>
                 <i class="help-tip icon-question-sign" href="#status"></i>
                 </span>
@@ -63,13 +63,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-                Team Lead:
+                <?php echo __('Team Lead');?>:
             </td>
             <td>
                 <span>
                 <select name="lead_id">
-                    <option value="0">&mdash; None &mdash;</option>
-                    <option value="" disabled="disabled">Select Team Lead (Optional)</option>
+                    <option value="0">&mdash; <?php echo __('None');?> &mdash;</option>
+                    <option value="" disabled="disabled"><?php echo __('Select Team Lead (Optional)');?></option>
                     <?php
                     if($team && ($members=$team->getMembers())){
                         foreach($members as $k=>$staff){
@@ -86,11 +86,11 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180">
-                Assignment Alert:
+                <?php echo __('Assignment Alert');?>:
             </td>
             <td>
                 <input type="checkbox" name="noalerts" value="1" <?php echo $info['noalerts']?'checked="checked"':''; ?> >
-                <strong>Disable</strong> for this Team
+                <?php echo __('<strong>Disable</strong> for this Team'); ?>
                 <i class="help-tip icon-question-sign" href="#assignment_alert"></i>
             </td>
         </tr>
@@ -98,16 +98,16 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         if($team && ($members=$team->getMembers())){ ?>
         <tr>
             <th colspan="2">
-                <em><strong>Team Members</strong>:
+                <em><strong><?php echo __('Team Members'); ?></strong>:
                 <i class="help-tip icon-question-sign" href="#members"></i>
 </em>
             </th>
         </tr>
         <?php
             foreach($members as $k=>$staff){
-                echo sprintf('<tr><td colspan=2><span style="width:350px;padding-left:5px; display:block; float:left;">
+                echo sprintf('<tr><td colspan=2><span style="width:350px;padding-left:5px; display:block;" class="pull-left">
                             <b><a href="staff.php?id=%d">%s</a></span></b>
-                            &nbsp;<input type="checkbox" name="remove[]" value="%d"><i>Remove</i></td></tr>',
+                            &nbsp;<input type="checkbox" name="remove[]" value="%d"><i>'.__('Remove').'</i></td></tr>',
                           $staff->getId(),$staff->getName(),$staff->getId());
 
 
@@ -115,7 +115,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         } ?>
         <tr>
             <th colspan="2">
-                <em><strong>Admin Notes</strong>: Internal notes viewable by all admins.&nbsp;</em>
+                <em><strong><?php echo __('Admin Notes');?></strong>: <?php echo __('Internal notes viewable by all admins.');?>&nbsp;</em>
             </th>
         </tr>
         <tr>
@@ -128,7 +128,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 </table>
 <p style="text-align:center">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="teams.php"'>
+    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="teams.php"'>
 </p>
 </form>
diff --git a/include/staff/teams.inc.php b/include/staff/teams.inc.php
index 410b738b305894377363f231b8982f127d9a7899..de08d25e88911eadac668e7c6f25e56610f42d2d 100644
--- a/include/staff/teams.inc.php
+++ b/include/staff/teams.inc.php
@@ -33,18 +33,18 @@ $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
 $query="$sql GROUP BY team.team_id ORDER BY $order_by";
 $res=db_query($query);
 if($res && ($num=db_num_rows($res)))
-    $showing="Showing 1-$num of $num teams";
+    $showing=sprintf(__('Showing 1-%1$d of %2$d teams'), $num, $num);
 else
-    $showing='No teams found!';
+    $showing=__('No teams found!');
 
 ?>
-<div style="width:700px;padding-top:5px; float:left;">
- <h2>Teams
+<div class="pull-left" style="width:700px;padding-top:5px;">
+ <h2><?php echo __('Teams');?>
     <i class="help-tip icon-question-sign" href="#teams"></i>
     </h2>
  </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
-    <b><a href="teams.php?a=add" class="Icon newteam">Add New Team</a></b></div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
+    <b><a href="teams.php?a=add" class="Icon newteam"><?php echo __('Add New Team');?></a></b></div>
 <div class="clear"></div>
 <form action="teams.php" method="POST" name="teams">
  <?php csrf_token(); ?>
@@ -55,12 +55,12 @@ else
     <thead>
         <tr>
             <th width="7px">&nbsp;</th>
-            <th width="250"><a <?php echo $name_sort; ?> href="teams.php?<?php echo $qstr; ?>&sort=name">Team Name</a></th>
-            <th width="80"><a  <?php echo $status_sort; ?> href="teams.php?<?php echo $qstr; ?>&sort=status">Status</a></th>
-            <th width="80"><a  <?php echo $members_sort; ?>href="teams.php?<?php echo $qstr; ?>&sort=members">Members</a></th>
-            <th width="200"><a  <?php echo $lead_sort; ?> href="teams.php?<?php echo $qstr; ?>&sort=lead">Team Lead</a></th>
-            <th width="100"><a  <?php echo $created_sort; ?> href="teams.php?<?php echo $qstr; ?>&sort=created">Created</a></th>
-            <th width="130"><a  <?php echo $updated_sort; ?> href="teams.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th>
+            <th width="250"><a <?php echo $name_sort; ?> href="teams.php?<?php echo $qstr; ?>&sort=name"><?php echo __('Team Name');?></a></th>
+            <th width="80"><a  <?php echo $status_sort; ?> href="teams.php?<?php echo $qstr; ?>&sort=status"><?php echo __('Status');?></a></th>
+            <th width="80"><a  <?php echo $members_sort; ?>href="teams.php?<?php echo $qstr; ?>&sort=members"><?php echo __('Members');?></a></th>
+            <th width="200"><a  <?php echo $lead_sort; ?> href="teams.php?<?php echo $qstr; ?>&sort=lead"><?php echo __('Team Lead');?></a></th>
+            <th width="100"><a  <?php echo $created_sort; ?> href="teams.php?<?php echo $qstr; ?>&sort=created"><?php echo __('Created');?></a></th>
+            <th width="130"><a  <?php echo $updated_sort; ?> href="teams.php?<?php echo $qstr; ?>&sort=updated"><?php echo __('Last Updated');?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -78,7 +78,7 @@ else
                   <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['team_id']; ?>"
                             <?php echo $sel?'checked="checked"':''; ?>> </td>
                 <td><a href="teams.php?id=<?php echo $row['team_id']; ?>"><?php echo $row['name']; ?></a> &nbsp;</td>
-                <td>&nbsp;<?php echo $row['isenabled']?'Active':'<b>Disabled</b>'; ?></td>
+                <td>&nbsp;<?php echo $row['isenabled']?__('Active'):'<b>'.__('Disabled').'</b>'; ?></td>
                 <td style="text-align:right;padding-right:25px">&nbsp;&nbsp;
                     <?php if($row['members']>0) { ?>
                         <a href="staff.php?tid=<?php echo $row['team_id']; ?>"><?php echo $row['members']; ?></a>
@@ -97,12 +97,12 @@ else
      <tr>
         <td colspan="7">
             <?php if($res && $num){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No teams found!';
+                echo __('No teams found!');
             } ?>
         </td>
      </tr>
@@ -112,36 +112,39 @@ else
 if($res && $num): //Show options..
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="enable" value="Enable" >
-    <input class="button" type="submit" name="disable" value="Disable" >
-    <input class="button" type="submit" name="delete" value="Delete" >
+    <input class="button" type="submit" name="enable" value="<?php echo __('Enable');?>" >
+    <input class="button" type="submit" name="disable" value="<?php echo __('Disable');?>" >
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete');?>" >
 </p>
 <?php
 endif;
 ?>
 </form>
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="enable-confirm">
-        Are you sure want to <b>enable</b> selected teams?
+        <?php echo sprintf(__('Are you sure want to <b>enable</b> %s?'),
+            _N('selected team', 'selected teams', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="disable-confirm">
-        Are you sure want to <b>disable</b> selected teams?
+        <?php echo sprintf(__('Are you sure want to <b>disable</b> %s?'),
+            _N('selected team', 'selected teams', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected teams?</strong></font>
-        <br><br>Deleted team CANNOT be recovered.
+        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
+            _N('selected team', 'selected teams', 2));?></strong></font>
+        <br><br><?php echo __('Deleted data CANNOT be recovered.'); ?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/template.inc.php b/include/staff/template.inc.php
index 6eefe948edb971c701744af16da066083e0a103d..475ac80b5d250c6ab30d57e70527822318b07e23 100644
--- a/include/staff/template.inc.php
+++ b/include/staff/template.inc.php
@@ -4,16 +4,16 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access
 $info=array();
 $qstr='';
 if($template && $_REQUEST['a']!='add'){
-    $title='Update Template';
+    $title=__('Update Template');
     $action='update';
-    $submit_text='Save Changes';
+    $submit_text=__('Save Changes');
     $info=$template->getInfo();
     $info['tpl_id']=$template->getId();
     $qstr.='&tpl_id='.$template->getId();
 }else {
-    $title='Add New Template';
+    $title=__('Add New Template');
     $action='add';
-    $submit_text='Add Template';
+    $submit_text=__('Add Template');
     $info['isactive']=isset($info['isactive'])?$info['isactive']:0;
     $info['lang_id'] = $cfg->getSystemLanguage();
     $qstr.='&a='.urlencode($_REQUEST['a']);
@@ -25,20 +25,20 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
  <input type="hidden" name="do" value="<?php echo $action; ?>">
  <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>">
  <input type="hidden" name="tpl_id" value="<?php echo $info['tpl_id']; ?>">
- <h2>Email Template</h2>
+ <h2><?php echo __('Email Template');?></h2>
  <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
         <tr>
             <th colspan="2">
                 <h4><?php echo $title; ?></h4>
-                <em>Template information.</em>
+                <em><?php echo __('Template information');?></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <td width="180" class="required">
-              Name:
+              <?php echo __('Name');?>:
             </td>
             <td>
                 <input type="text" size="30" name="name" value="<?php echo $info['name']; ?>">
@@ -47,13 +47,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         </tr>
         <tr>
             <td width="180" class="required">
-                Status:
+                <?php echo __('Status');?>:
             </td>
             <td>
                 <span>
-                <label><input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong>&nbsp;Enabled</strong></label>
+                <label><input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong>&nbsp;<?php echo __('Enabled'); ?></strong></label>
                 &nbsp;
-                <label><input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>>&nbsp;Disabled</label>
+                <label><input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>>&nbsp;<?php echo __('Disabled'); ?></label>
                 &nbsp;<span class="error">*&nbsp;<?php echo $errors['isactive']; ?></span>&nbsp;<i class="help-tip icon-question-sign" href="#status"></i>
                 </span>
             </td>
@@ -62,7 +62,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         if($template){ ?>
         <tr>
             <td width="180" class="required">
-                Language:
+                <?php echo __('Language');?>:
             </td>
             <td><?php
             echo Internationalization::getLanguageDescription($info['lang']);
@@ -85,27 +85,27 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             <th colspan="2">
             <em><strong><?php echo isset($_groups[$current_group])
             ? $_groups[$current_group] : $current_group; ?></strong>
-            :: Click on the title to edit.&nbsp;</em>
+            :: <?php echo __('Click on the title to edit.'); ?></em>
             </th>
         </tr>
 <?php } # end if ($current_group)
             if (isset($impl[$cn])) {
-                echo sprintf('<tr><td colspan="2">&nbsp;<strong><a href="templates.php?id=%d&a=manage">%s</a></strong>, <span class="faded">Updated %s</span><br/>&nbsp;%s</td></tr>',
-                $impl[$cn]->getId(), Format::htmlchars($info['name']),
-                Format::db_datetime($impl[$cn]->getLastUpdated()),
-                Format::htmlchars($info['desc']));
+                echo sprintf('<tr><td colspan="2">&nbsp;<strong><a href="templates.php?id=%d&a=manage">%s</a></strong>, <span class="faded">%s</span><br/>&nbsp;%s</td></tr>',
+                $impl[$cn]->getId(), Format::htmlchars(__($info['name'])),
+                sprintf(__('Updated %s'), Format::db_datetime($impl[$cn]->getLastUpdated())),
+                Format::htmlchars(__($info['desc'])));
             } else {
                 echo sprintf('<tr><td colspan=2>&nbsp;<strong><a
                     href="templates.php?tpl_id=%d&a=implement&code_name=%s"
                     >%s</a></strong><br/>&nbsp%s</td></tr>',
-                    $template->getid(),$cn,format::htmlchars($info['name']),
-                    format::htmlchars($info['desc']));
+                    $template->getid(),$cn,format::htmlchars(__($info['name'])),
+                    format::htmlchars(__($info['desc'])));
             }
          } # endfor
         } else { ?>
         <tr>
             <td width="180" class="required">
-                Template Set To Clone:
+                <?php echo __('Template Set To Clone');?>:
             </td>
             <td>
                 <select name="tpl_id" onchange="javascript:
@@ -114,7 +114,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
     else
         $('#language').hide();
 ">
-                    <option value="0">&mdash; Stock Templates &dash;</option>
+                    <option value="0">&mdash; <?php echo __('Stock Templates'); ?> &mdash;</option>
                     <?php
                     $sql='SELECT tpl_id,name FROM '.EMAIL_TEMPLATE_GRP_TABLE.' ORDER by name';
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -132,7 +132,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 <tbody id="language">
         <tr>
             <td width="180" class="required">
-                Language:
+                <?php echo __('Language'); ?>:
             </td>
             <td>
         <?php
@@ -153,7 +153,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <?php } ?>
         <tr>
             <th colspan="2">
-                <em><strong>Admin Notes </strong>: Internal notes.</em>
+                <em><strong><?php echo __('Internal Notes');?></strong>: <?php echo __(
+                "be liberal, they're internal");?></em>
             </th>
         </tr>
         <tr>
@@ -166,7 +167,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
 </table>
 <p style="text-align:center">
     <input type="submit" name="submit" value="<?php echo $submit_text; ?>">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="templates.php"'>
+    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="templates.php"'>
 </p>
 </form>
diff --git a/include/staff/templates.inc.php b/include/staff/templates.inc.php
index 0ce527d8b3c1301bb9d3d39f25c8d2a1eb05edbb..3fb2c49b5fab00a2953940c18e248e1e68e6c17c 100644
--- a/include/staff/templates.inc.php
+++ b/include/staff/templates.inc.php
@@ -36,17 +36,17 @@ $qstr.='&order='.($order=='DESC'?'ASC':'DESC');
 $query="$sql GROUP BY tpl.tpl_id ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
 $res=db_query($query);
 if($res && ($num=db_num_rows($res)))
-    $showing=$pageNav->showing().' Templates';
+    $showing=$pageNav->showing().' '._N('template', 'templates', $num);
 else
-    $showing='No templates found!';
+    $showing=__('No templates found!');
 
 ?>
 
-<div style="width:700px;padding-top:5px; float:left;">
- <h2>Email Template Sets</h2>
+<div class="pull-left" style="width:700px;padding-top:5px;">
+ <h2><?php echo __('Email Template Sets'); ?></h2>
 </div>
-<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;">
- <b><a href="templates.php?a=add" class="Icon newEmailTemplate">Add New Template Set</a></b></div>
+<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;">
+ <b><a href="templates.php?a=add" class="Icon newEmailTemplate"><?php echo __('Add New Template Set'); ?></a></b></div>
 <div class="clear"></div>
 <form action="templates.php" method="POST" name="tpls">
  <?php csrf_token(); ?>
@@ -57,11 +57,11 @@ else
     <thead>
         <tr>
             <th width="7">&nbsp;</th>
-            <th width="350"><a <?php echo $name_sort; ?> href="templates.php?<?php echo $qstr; ?>&sort=name">Name</a></th>
-            <th width="100"><a  <?php echo $status_sort; ?> href="templates.php?<?php echo $qstr; ?>&sort=status">Status</a></th>
-            <th width="80"><a <?php echo $inuse_sort; ?> href="templates.php?<?php echo $qstr; ?>&sort=inuse">In-Use</a></th>
-            <th width="120" nowrap><a  <?php echo $created_sort; ?>href="templates.php?<?php echo $qstr; ?>&sort=created">Date Added</a></th>
-            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="templates.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th>
+            <th width="350"><a <?php echo $name_sort; ?> href="templates.php?<?php echo $qstr; ?>&sort=name"><?php echo __('Name'); ?></a></th>
+            <th width="100"><a  <?php echo $status_sort; ?> href="templates.php?<?php echo $qstr; ?>&sort=status"><?php echo __('Status'); ?></a></th>
+            <th width="80"><a <?php echo $inuse_sort; ?> href="templates.php?<?php echo $qstr; ?>&sort=inuse"><?php echo __('In-Use'); ?></a></th>
+            <th width="120" nowrap><a  <?php echo $created_sort; ?>href="templates.php?<?php echo $qstr; ?>&sort=created"><?php echo __('Date Added'); ?></a></th>
+            <th width="150" nowrap><a  <?php echo $updated_sort; ?>href="templates.php?<?php echo $qstr; ?>&sort=updated"><?php echo __('Last Updated'); ?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -76,7 +76,7 @@ else
                 if($ids && in_array($row['tpl_id'],$ids))
                     $sel=true;
 
-                $default=($defaultTplId==$row['tpl_id'])?'<small class="fadded">(System Default)</small>':'';
+                $default=($defaultTplId==$row['tpl_id'])?'<small class="fadded">('.__('System Default').')</small>':'';
                 ?>
             <tr id="<?php echo $row['tpl_id']; ?>">
                 <td width=7px>
@@ -85,8 +85,8 @@ else
                 </td>
                 <td>&nbsp;<a href="templates.php?tpl_id=<?php echo $row['tpl_id']; ?>"><?php echo Format::htmlchars($row['name']); ?></a>
                 &nbsp;<?php echo $default; ?></td>
-                <td>&nbsp;<?php echo $row['isactive']?'Active':'<b>Disabled</b>'; ?></td>
-                <td>&nbsp;&nbsp;<?php echo ($inuse)?'<b>Yes</b>':'No'; ?></td>
+                <td>&nbsp;<?php echo $row['isactive']?__('Active'):'<b>'.__('Disabled').'</b>'; ?></td>
+                <td>&nbsp;&nbsp;<?php echo ($inuse)?'<b>'.__('Yes').'</b>':__('No'); ?></td>
                 <td>&nbsp;<?php echo Format::db_date($row['created']); ?></td>
                 <td>&nbsp;<?php echo Format::db_datetime($row['updated']); ?></td>
             </tr>
@@ -97,12 +97,12 @@ else
      <tr>
         <td colspan="6">
             <?php if($res && $num){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
             <?php }else{
-                echo 'No templates found';
+                echo __('No templates found!');
             } ?>
         </td>
      </tr>
@@ -110,12 +110,12 @@ else
 </table>
 <?php
 if($res && $num): //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 ?>
 <p class="centered" id="actions">
-    <input class="button" type="submit" name="enable" value="Enable" >
-    <input class="button" type="submit" name="disable" value="Disable" >
-    <input class="button" type="submit" name="delete" value="Delete" >
+    <input class="button" type="submit" name="enable" value="<?php echo __('Enable');?>" >
+    <input class="button" type="submit" name="disable" value="<?php echo __('Disable');?>" >
+    <input class="button" type="submit" name="delete" value="<?php echo __('Delete');?>" >
 </p>
 <?php
 endif;
@@ -123,27 +123,30 @@ endif;
 </form>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="enable-confirm">
-        Are you sure want to <b>enable</b> selected templates?
+        <?php echo sprintf(__('Are you sure want to <b>enable</b> %s?'),
+            _N('selected template set', 'selected template sets', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="disable-confirm">
-        Are you sure want to <b>disable</b>  selected templates?
+        <?php echo sprintf(__('Are you sure want to <b>disable</b> %s?'),
+            _N('selected template set', 'selected template sets', 2));?>
     </p>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected templates?</strong></font>
-        <br><br>Deleted templates CANNOT be recovered.
+        <font color="red"><strong><?php echo sprintf(__('Are you sure you want to DELETE %s?'),
+            _N('selected template set', 'selected template sets', 2));?></strong></font>
+        <br><br><?php echo __('Deleted data CANNOT be recovered.'); ?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/templates/collaborators-preview.tmpl.php b/include/staff/templates/collaborators-preview.tmpl.php
index db8626ab2c461250e55208cbd11e51d22996af94..964c225e7db174f8e0a614889104378f14d7e312 100644
--- a/include/staff/templates/collaborators-preview.tmpl.php
+++ b/include/staff/templates/collaborators-preview.tmpl.php
@@ -12,7 +12,7 @@ if (($users=$ticket->getCollaborators())) {?>
                 $user->getEmail());
     }
 }  else {
-    echo "<strong>Ticket doesn't have collaborators.</strong>";
+    echo "<strong>".__("Ticket doesn't have any collaborators.")."</strong>";
 }?>
 </table>
 <?php
@@ -22,7 +22,7 @@ $options[] = sprintf(
         '<a class="collaborators" id="managecollab" href="#tickets/%d/collaborators">%s</a>',
         $ticket->getId(),
         $ticket->getNumCollaborators()
-        ? 'Manage Collaborators' : 'Add Collaborator'
+        ? __('Manage Collaborators') : __('Add Collaborator')
         );
 
 if ($options) {
diff --git a/include/staff/templates/collaborators.tmpl.php b/include/staff/templates/collaborators.tmpl.php
index d2bbc871afe6ec0fc2fb7b684996fbedcf5e93eb..d8d339c882dae25b1f5592f43a40c95420e76f66 100644
--- a/include/staff/templates/collaborators.tmpl.php
+++ b/include/staff/templates/collaborators.tmpl.php
@@ -1,4 +1,4 @@
-<h3>Ticket Collaborators</h3>
+<h3><?php echo __('Ticket Collaborators'); ?></h3>
 <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b>
 <?php
 if($info && $info['msg']) {
@@ -36,15 +36,17 @@ if(($users=$ticket->getCollaborators())) {?>
     </table>
     <hr style="margin-top:1em"/>
     <div><a class="collaborator"
-        href="#tickets/<?php echo $ticket->getId(); ?>/add-collaborator" >Add New Collaborator</a></div>
-    <div id="savewarning" style="display:none; padding-top:2px;"><p id="msg_warning">You have made changes that you need to save.</p></div>
+        href="#tickets/<?php echo $ticket->getId(); ?>/add-collaborator"
+        ><i class="icon-plus-sign"></i> <?php echo __('Add New Collaborator'); ?></a></div>
+    <div id="savewarning" style="display:none; padding-top:2px;"><p
+    id="msg_warning"><?php echo __('You have made changes that you need to save.'); ?></p></div>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="Cancel" class="close">
-            <input type="reset" value="Reset">
+        <span class="buttons pull-left">
+            <input type="reset" value="<?php echo __('Reset'); ?>">
+            <input type="button" value="<?php echo __('Cancel'); ?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="submit" value="Save Changes">
+        <span class="buttons pull-right">
+        <input type="submit" value="<?php echo __('Save Changes'); ?>">
         </span>
      </p>
 </form>
@@ -52,11 +54,11 @@ if(($users=$ticket->getCollaborators())) {?>
 </div>
 <?php
 } else {
-    echo "Bro, not sure how you got here!";
+    echo __("Bro, not sure how you got here!");
 }
 
 if ($_POST && $ticket && $ticket->getNumCollaborators()) {
-    $recipients = sprintf('Recipients (%d of %d)',
+    $recipients = sprintf(__('Recipients (%d of %d)'),
           $ticket->getNumActiveCollaborators(),
           $ticket->getNumCollaborators());
     ?>
diff --git a/include/staff/templates/content-manage.tmpl.php b/include/staff/templates/content-manage.tmpl.php
index d2a546cd0367b02b9c4defb3f63c8531200972c8..ddf9892ad4a5eef850ac4927571538f7580ec9c0 100644
--- a/include/staff/templates/content-manage.tmpl.php
+++ b/include/staff/templates/content-manage.tmpl.php
@@ -1,4 +1,4 @@
-<h3>Manage Content &mdash; <?php echo Format::htmlchars($content->getName()); ?></h3>
+<h3><?php echo __('Manage Content'); ?> &mdash; <?php echo Format::htmlchars($content->getName()); ?></h3>
 <a class="close" href=""><i class="icon-remove-circle"></i></a>
 <hr/>
 <form method="post" action="#content/<?php echo $content->getId(); ?>">
@@ -13,12 +13,13 @@
 echo $content->getNotes(); ?></div>
     <hr/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="reset" value="Reset">
-            <input type="button" name="cancel" class="<?php echo $user ? 'cancel' : 'close' ?>"  value="Cancel">
+        <span class="buttons pull-left">
+            <input type="reset" value="<?php echo __('Reset'); ?>">
+            <input type="button" name="cancel" class="<?php
+                echo $user ? 'cancel' : 'close'; ?>" value="<?php echo __('Cancel'); ?>">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="submit" value="Save Changes">
+        <span class="buttons pull-right">
+            <input type="submit" value="<?php echo __('Save Changes'); ?>">
         </span>
      </p>
 </form>
diff --git a/include/staff/templates/dynamic-field-config.tmpl.php b/include/staff/templates/dynamic-field-config.tmpl.php
index a295041c4787f2427ca5d223abeb7d02c1224e00..51701b6ee1f66ce93c66fba5845a14abfd65d78b 100644
--- a/include/staff/templates/dynamic-field-config.tmpl.php
+++ b/include/staff/templates/dynamic-field-config.tmpl.php
@@ -1,79 +1,62 @@
-    <h3>Field Configuration &mdash; <?php echo $field->get('label') ?></h3>
+    <h3><?php echo __('Field Configuration'); ?> &mdash; <?php echo $field->get('label') ?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
-    <form method="post" action="ajax.php/form/field-config/<?php
-            echo $field->get('id'); ?>" onsubmit="javascript:
-            var form = $(this);
-            $.post(this.action, form.serialize(), function(data, status, xhr) {
-                    if (!data.length) {
-                        form.closest('.dialog').hide();
-                        $('#overlay').hide();
-                    } else {
-                        form.closest('.dialog').empty().append(data);
-                    }
-            });
-            return false;
-            ">
-        <table width="100%">
+    <form method="post" action="#form/field-config/<?php
+            echo $field->get('id'); ?>">
         <?php
         echo csrf_token();
-        $config = $field->getConfiguration();
-        foreach ($field->getConfigurationForm() as $name=>$f) {
-            if (isset($config[$name]))
-                $f->value = $config[$name];
-            else if ($f->get('default'))
-                $f->value = $f->get('default');
-            ?>
-            <tr><td class="multi-line">
-            <label for="<?php echo $f->getWidget()->name; ?>"
-                style="vertical-align:top;padding-top:0.2em">
-                <?php echo Format::htmlchars($f->get('label')); ?>:</label>
-            </td><td>
-            <span style="display:inline-block">
-            <?php
-            $f->render();
-            if ($f->get('required')) { ?>
-                <font class="error">*</font>
+        $form = $field->getConfigurationForm();
+        echo $form->getMedia();
+        foreach ($form->getFields() as $name=>$f) { ?>
+            <div class="flush-left custom-field" id="field<?php echo $f->getWidget()->id;
+                ?>" <?php if (!$f->isVisible()) echo 'style="display:none;"'; ?>>
+            <div class="field-label <?php if ($f->get('required')) echo 'required'; ?>">
+            <label for="<?php echo $f->getWidget()->name; ?>">
+                <?php echo Format::htmlchars($f->get('label')); ?>:
+      <?php if ($f->get('required')) { ?>
+                <span class="error">*</span>
+      <?php } ?>
+            </label>
             <?php
-            }
             if ($f->get('hint')) { ?>
-                <br /><em style="color:gray;display:inline-block"><?php
+                <br/><em style="color:gray;display:inline-block"><?php
                     echo Format::htmlchars($f->get('hint')); ?></em>
             <?php
-            }
+            } ?>
+            </div><div>
+            <?php
+            $f->render();
             ?>
-            </span>
+            </div>
             <?php
             foreach ($f->errors() as $e) { ?>
-                <br />
-                <font class="error"><?php echo $e; ?></font>
+                <div class="error"><?php echo $e; ?></div>
             <?php } ?>
-            </td></tr>
-            <?php
-        }
+            </div>
+        <?php }
         ?>
-        <tr><td colspan="2"><hr/></td></tr>
-        <tr><td class="multi-line">
+        <hr/>
+        <div class="flush-left custom-field">
+        <div class="field-label">
         <label for="hint"
-            style="vertical-align:top;padding-top:0.2em">Help Text:</label>
-        </td><td>
-        <span style="display:inline-block">
-        <textarea name="hint" rows="2" cols="40"><?php
-            echo Format::htmlchars($field->get('hint')); ?></textarea>
+            style="vertical-align:top;padding-top:0.2em"><?php echo __('Help Text') ?>:</label>
             <br />
             <em style="color:gray;display:inline-block">
-                Help text shown with the field</em>
-        </span>
-        </td></tr>
-        </table>
+                <?php echo __('Help text shown with the field'); ?></em>
+        </div>
+        <div>
+        <textarea style="width:100%" name="hint" rows="2" cols="40"><?php
+            echo Format::htmlchars($field->get('hint')); ?></textarea>
+        </div>
+        </div>
         <hr>
         <p class="full-width">
-            <span class="buttons" style="float:left">
-                <input type="reset" value="Reset">
-                <input type="button" value="Cancel" class="close">
+            <span class="buttons pull-left">
+                <input type="reset" value="<?php echo __('Reset'); ?>">
+                <input type="button" value="<?php echo __('Cancel'); ?>" class="close">
             </span>
-            <span class="buttons" style="float:right">
-                <input type="submit" value="Save">
+            <span class="buttons pull-right">
+                <input type="submit" value="<?php echo __('Save'); ?>">
             </span>
          </p>
     </form>
diff --git a/include/staff/templates/dynamic-form.tmpl.php b/include/staff/templates/dynamic-form.tmpl.php
index d44d3062b27cdbb497f2242dec90ba2eb1e2e709..3987d9ee0ab9582db3fbcd57d669621477da6846 100644
--- a/include/staff/templates/dynamic-form.tmpl.php
+++ b/include/staff/templates/dynamic-form.tmpl.php
@@ -37,6 +37,13 @@ if (isset($options['entry']) && $options['mode'] == 'edit') { ?>
     <?php
     }
     foreach ($form->getFields() as $field) {
+        try {
+            if (!$field->isVisibleToStaff())
+                continue;
+        }
+        catch (Exception $e) {
+            // Not connected to a DynamicFormField
+        }
         ?>
         <tr><?php if ($field->isBlockLevel()) { ?>
                 <td colspan="2">
@@ -55,9 +62,9 @@ if (isset($options['entry']) && $options['mode'] == 'edit') { ?>
             <?php
             }
             if (($a = $field->getAnswer()) && $a->isDeleted()) {
-                ?><a class="action-button danger overlay" title="Delete this data"
+                ?><a class="action-button float-right danger overlay" title="Delete this data"
                     href="#delete-answer"
-                    onclick="javascript:if (confirm('You sure?'))
+                    onclick="javascript:if (confirm('<?php echo __('You sure?'); ?>'))
                         $.ajax({
                             url: 'ajax.php/form/answer/'
                                 +$(this).data('entryId') + '/' + $(this).data('fieldId'),
diff --git a/include/staff/templates/form-manage.tmpl.php b/include/staff/templates/form-manage.tmpl.php
index 52f670e91f42b9b3b848b3c1d0da36d763212434..378fad751e0b7d52b019b17c7ffd9b2bd5030a4f 100644
--- a/include/staff/templates/form-manage.tmpl.php
+++ b/include/staff/templates/form-manage.tmpl.php
@@ -1,8 +1,8 @@
-<h3><i class="icon-paste"></i> Manage Forms</i></h3>
+<h3><i class="icon-paste"></i> <?php echo __('Manage Forms'); ?></i></h3>
 <b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b>
-<hr/>
-Sort the forms on this ticket by click and dragging on them. Use the box
-below the forms list to add new forms to the ticket.
+<hr/><?php echo __(
+'Sort the forms on this ticket by click and dragging on them. Use the box below the forms list to add new forms to the ticket.'
+); ?>
 <br/>
 <br/>
 <form method="post" action="<?php echo $info['action']; ?>">
@@ -10,12 +10,14 @@ below the forms list to add new forms to the ticket.
 <?php
 $current_list = array();
 foreach ($forms as $e) { ?>
-<div class="sortable-row-item" data-id="<?php echo $e->get('id'); ?>">
+<div class="sortable row-item" data-id="<?php echo $e->get('id'); ?>">
     <input type="hidden" name="forms[]" value="<?php echo $e->get('form_id'); ?>" />
     <i class="icon-reorder"></i> <?php echo $e->getForm()->getTitle();
     $current_list[] = $e->get('form_id');
     if ($e->getForm()->get('type') == 'G') { ?>
+    <div class="button-group">
     <div class="delete"><a href="#"><i class="icon-trash"></i></a></div>
+    </div>
     <?php } ?>
 </div>
 <?php } ?>
@@ -24,17 +26,19 @@ foreach ($forms as $e) { ?>
 <i class="icon-plus"></i>&nbsp;
 <select name="new-form" onchange="javascript:
     var $sel = $(this).find('option:selected');
-    $('#ticket-entries').append($('<div></div>').addClass('sortable-row-item')
+    $('#ticket-entries').append($('<div></div>').addClass('sortable row-item')
         .text(' '+$sel.text())
         .data('id', $sel.val())
         .prepend($('<i>').addClass('icon-reorder'))
         .append($('<input/>').attr({name:'forms[]', type:'hidden'}).val($sel.val()))
-        .append($('<div></div>').addClass('delete')
+        .append($('<div></div>').addClass('button-group')
+          .append($('<div></div>').addClass('delete')
             .append($('<a href=\'#\'>').append($('<i>').addClass('icon-trash')))
-        )
+        ))
     );
     $sel.prop('disabled',true);">
-<option selected="selected" disabled="disabled">Add a new form to this ticket</option>
+<option selected="selected" disabled="disabled"><?php
+    echo __('Add a form'); ?></option>
 <?php foreach (DynamicForm::objects()->filter(array(
     'type'=>'G')) as $f
 ) {
@@ -46,19 +50,20 @@ foreach ($forms as $e) { ?>
 </select>
 <div id="delete-warning" style="display:none">
 <hr>
-    <div id="msg_warning">
-    Clicking <strong>Save Changes</strong> will permanently delete data
-    associated with the deleted forms
+    <div id="msg_warning"><?php echo __(
+    'Clicking <strong>Save Changes</strong> will permanently delete data associated with the deleted forms'
+    ); ?>
     </div>
 </div>
     <hr>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="reset" value="Reset">
-            <input type="button" name="cancel" class="<?php echo $user ? 'cancel' : 'close' ?>"  value="Cancel">
+        <span class="buttons pull-left">
+            <input type="reset" value="<?php echo __('Reset'); ?>">
+            <input type="button" name="cancel" class="<?php
+                echo $user ? 'cancel' : 'close' ?>" value="<?php echo __('Cancel'); ?>">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="submit" value="Save Changes">
+        <span class="buttons pull-right">
+            <input type="submit" value="<?php echo __('Save Changes'); ?>">
         </span>
      </p>
 
@@ -66,7 +71,7 @@ foreach ($forms as $e) { ?>
 $(function() {
     $('#ticket-entries').sortable({containment:'parent',tolerance:'pointer'});
     $('#ticket-entries .delete a').live('click', function() {
-        var $div = $(this).closest('.sortable-row-item');
+        var $div = $(this).closest('.sortable.row-item');
         $('select[name=new-form]').find('option[data-id='+$div.data('id')+']')
             .prop('disabled',false);
         $div.remove();
diff --git a/include/staff/templates/list-item-properties.tmpl.php b/include/staff/templates/list-item-properties.tmpl.php
index ce4a0ca5ee64aeb1ccef1914cea444f21f1c1390..dcfc34b92d5bb7f51497cd227a13a2f609a79bc6 100644
--- a/include/staff/templates/list-item-properties.tmpl.php
+++ b/include/staff/templates/list-item-properties.tmpl.php
@@ -1,67 +1,61 @@
-    <h3>Item Properties &mdash; <?php echo $item->get('value') ?></h3>
+    <h3><?php echo __('Item Properties'); ?> &mdash; <?php echo $item->getValue() ?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
-    <form method="post" action="ajax.php/list/item/<?php
-            echo $item->get('id'); ?>/properties" onsubmit="javascript:
-            var form = $(this);
-            $.post(this.action, form.serialize(), function(data, status, xhr) {
-                if (!data.length) {
-                    form.closest('.dialog').hide();
-                    $('#overlay').hide();
-                } else {
-                    form.closest('.dialog').empty().append(data);
-                }
-            });
-            return false;">
-        <table width="100%" class="fixed">
-        <tr><td style="width:120px"></td><td></td></tr>
+    <form method="post" action="#list/<?php
+            echo $list->getId(); ?>/item/<?php
+            echo $item->getId(); ?>/properties">
         <?php
         echo csrf_token();
         $config = $item->getConfiguration();
-        foreach ($item->getConfigurationForm()->getFields() as $f) {
-            $name = $f->get('id');
-            if (isset($config[$name]))
-                $f->value = $config[$name];
-            else if ($f->get('default'))
-                $f->value = $f->get('default');
+        $internal = $item->isInternal();
+        $form = $item->getConfigurationForm();
+        echo $form->getMedia();
+        foreach ($form->getFields() as $f) {
             ?>
-            <tr><td class="multi-line">
+            <div class="custom-field" id="field<?php
+                echo $f->getWidget()->id; ?>"
+                <?php
+                if (!$f->isVisible()) echo 'style="display:none;"'; ?>>
+            <div class="field-label">
             <label for="<?php echo $f->getWidget()->name; ?>"
                 style="vertical-align:top;padding-top:0.2em">
                 <?php echo Format::htmlchars($f->get('label')); ?>:</label>
-            </td><td>
-            <span style="display:inline-block;width:100%">
-            <?php
-            $f->render();
-            if ($f->get('required')) { ?>
-                <font class="error">*</font>
-            <?php
-            }
-            if ($f->get('hint')) { ?>
-                <br /><em style="color:gray;display:inline-block"><?php
-                    echo Format::htmlchars($f->get('hint')); ?></em>
+                <?php
+                if (!$internal && $f->isEditable() && $f->get('hint')) { ?>
+                    <br /><em style="color:gray;display:inline-block"><?php
+                        echo Format::htmlchars($f->get('hint')); ?></em>
+                <?php
+                } ?>
+            </div><div>
             <?php
+            if ($internal && !$f->isEditable())
+                $f->render('view');
+            else {
+                $f->render();
+                if ($f->get('required')) { ?>
+                    <font class="error">*</font>
+                <?php
+                }
             }
             ?>
-            </span>
+            </div>
             <?php
             foreach ($f->errors() as $e) { ?>
-                <br />
-                <font class="error"><?php echo $e; ?></font>
+                <div class="error"><?php echo $e; ?></div>
             <?php } ?>
-            </td></tr>
+            </div>
             <?php
         }
         ?>
         </table>
         <hr>
         <p class="full-width">
-            <span class="buttons" style="float:left">
-                <input type="reset" value="Reset">
-                <input type="button" value="Cancel" class="close">
+            <span class="buttons pull-left">
+                <input type="reset" value="<?php echo __('Reset'); ?>">
+                <input type="button" value="<?php echo __('Cancel'); ?>" class="close">
             </span>
-            <span class="buttons" style="float:right">
-                <input type="submit" value="Save">
+            <span class="buttons pull-right">
+                <input type="submit" value="<?php echo __('Save'); ?>">
             </span>
          </p>
     </form>
diff --git a/include/staff/templates/notes.tmpl.php b/include/staff/templates/notes.tmpl.php
index 1991c03b7c84a94e236ba882dcd18797f08abdf9..4ae5e1a9ee45880d0071bb5b951283092e31a334 100644
--- a/include/staff/templates/notes.tmpl.php
+++ b/include/staff/templates/notes.tmpl.php
@@ -8,7 +8,8 @@ foreach ($notes as $note) {
 <div id="new-note-box">
 <div class="quicknote" id="new-note" data-url="<?php echo $create_note_url; ?>">
 <div class="body">
-    <a href="#"><i class="icon-plus icon-large"></i> &nbsp; Click to create a new note</a>
+    <a href="#"><i class="icon-plus icon-large"></i> &nbsp;
+    <?php echo __('Click to create a new note'); ?></a>
 </div>
 </div>
 </div>
diff --git a/include/staff/templates/org-delete.tmpl.php b/include/staff/templates/org-delete.tmpl.php
index 5b00aee559e84c5189e5c9317e0fa2b3a0c0eee7..16e06c83b556e6bb2a034ba46d38c5c4834e3ec7 100644
--- a/include/staff/templates/org-delete.tmpl.php
+++ b/include/staff/templates/org-delete.tmpl.php
@@ -1,9 +1,9 @@
 <?php
 
 if (!$info['title'])
-    $info['title'] = 'Delete '.Format::htmlchars($org->getName());
+    $info['title'] = sprintf(__('Delete %s'), Format::htmlchars($org->getName()));
 
-$info['warn'] = 'Deleted organization CANNOT be recovered';
+$info['warn'] = __('Deleted organization CANNOT be recovered');
 
 ?>
 <h3><?php echo $info['title']; ?></h3>
@@ -40,8 +40,9 @@ if ($info['error']) {
     <?php
     if (($users=$org->users->count())) { ?>
     <hr>
-    <div>&nbsp;<strong><?php echo sprintf('%d %s', $users, $users>1 ? 'users' : 'user');
-        ?> assigned to this organization will be orphaned.</strong></div>
+    <div>&nbsp;<strong><?php echo sprintf(__(
+            '%s assigned to this organization will be orphaned.'),
+            sprintf(_N('One user', '%d users', $users), $users)); ?></strong></div>
     <?php
     } ?>
     <hr>
@@ -49,13 +50,13 @@ if ($info['error']) {
         action="#orgs/<?php echo $org->getId(); ?>/delete">
         <input type="hidden" name="id" value="<?php echo $org->getId(); ?>" />
         <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="reset" value="Reset">
+        <span class="buttons pull-left">
+            <input type="reset" value="<?php echo __('Reset'); ?>">
             <input type="button" name="cancel" class="close"
-                value="No, Cancel">
+                value="<?php echo __('No, Cancel'); ?>">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="submit" value="Yes, Delete">
+        <span class="buttons pull-right">
+            <input type="submit" value="<?php echo __('Yes, Delete'); ?>">
         </span>
         </p>
     </form>
diff --git a/include/staff/templates/org-lookup.tmpl.php b/include/staff/templates/org-lookup.tmpl.php
index 370310dd13e217f038927e83d55190cf687a6bbb..10a4ce28fbf75916ba718e52e45bd0c9f38ae25a 100644
--- a/include/staff/templates/org-lookup.tmpl.php
+++ b/include/staff/templates/org-lookup.tmpl.php
@@ -1,11 +1,11 @@
 <?php
 
 if (!$info['title'])
-    $info['title'] = 'Organization Lookup';
+    $info['title'] = __('Organization Lookup');
 
-$msg_info = 'Search existing organizations or add a new one.';
+$msg_info = __('Search existing organizations or add a new one.');
 if ($info['search'] === false)
-    $msg_info = 'Complete the form below to add a new organization.';
+    $msg_info = __('Complete the form below to add a new organization.');
 
 ?>
 <div id="the-lookup-form">
@@ -34,7 +34,8 @@ if ($info['error']) {
     <input type="hidden" id="org-id" name="orgid" value="<?php echo $org ? $org->getId() : 0; ?>"/>
     <i class="icon-group icon-4x pull-left icon-border"></i>
     <a class="action-button pull-right" style="overflow:inherit"
-        id="unselect-org"  href="#"><i class="icon-remove"></i> Add New Organization</a>
+        id="unselect-org"  href="#"><i class="icon-remove"></i>
+        <?php echo __('Add New Organization'); ?></a>
     <div><strong id="org-name"><?php echo $org ?  Format::htmlchars($org->getName()) : ''; ?></strong></div>
 <?php if ($org) { ?>
     <table style="margin-top: 1em;">
@@ -54,11 +55,11 @@ if ($info['error']) {
 <div class="clear"></div>
 <hr>
 <p class="full-width">
-    <span class="buttons" style="float:left">
-        <input type="button" name="cancel" class="close"  value="Cancel">
+    <span class="buttons pull-left">
+        <input type="button" name="cancel" class="close"  value="<?php echo __('Cancel'); ?>">
     </span>
-    <span class="buttons" style="float:right">
-        <input type="submit" value="Continue">
+    <span class="buttons pull-right">
+        <input type="submit" value="<?php echo __('Continue'); ?>">
     </span>
  </p>
 </form>
@@ -68,16 +69,17 @@ if ($info['error']) {
     <table width="100%" class="fixed">
     <?php
         if (!$form) $form = OrganizationForm::getInstance();
-        $form->render(true, 'Create New Organization'); ?>
+        $form->render(true, __('Create New Organization')); ?>
     </table>
     <hr>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="reset" value="Reset">
-            <input type="button" name="cancel" class="<?php echo $org ? 'cancel' : 'close' ?>"  value="Cancel">
+        <span class="buttons pull-left">
+            <input type="reset" value="<?php echo __('Reset'); ?>">
+            <input type="button" name="cancel" class="<?php echo $org ? 'cancel' : 'close' ?>"
+                value="<?php echo __('Cancel'); ?>">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="submit" value="Add Organization">
+        <span class="buttons pull-right">
+            <input type="submit" value="<?php echo __('Add Organization'); ?>">
         </span>
      </p>
 </form>
diff --git a/include/staff/templates/org-profile.tmpl.php b/include/staff/templates/org-profile.tmpl.php
index 3dda76484b0a7e0432957b2045eff10b83580f9f..2b83f76b602a1fdbf32149957eb6118ed865efa1 100644
--- a/include/staff/templates/org-profile.tmpl.php
+++ b/include/staff/templates/org-profile.tmpl.php
@@ -17,9 +17,10 @@ if ($info['error']) {
 } ?>
 <ul class="tabs">
     <li><a href="#tab-profile" class="active"
-        ><i class="icon-edit"></i>&nbsp;Fields</a></li>
+        ><i class="icon-edit"></i>&nbsp;<?php echo __('Fields'); ?></a></li>
     <li><a href="#contact-settings"
-        ><i class="icon-fixed-width icon-cogs faded"></i>&nbsp;Settings</a></li>
+        ><i class="icon-fixed-width icon-cogs faded"></i>&nbsp;<?php
+        echo __('Settings'); ?></a></li>
 </ul>
 <form method="post" class="org" action="<?php echo $action; ?>">
 
@@ -44,13 +45,15 @@ if ($ticket && $ticket->getOwnerId() == $user->getId())
         <tbody>
             <tr>
                 <td width="180">
-                    Account Manager:
+                    <?php echo __('Account Manager'); ?>:
                 </td>
                 <td>
                     <select name="manager">
-                        <option value="0" selected="selected">&mdash; None &mdash;</option><?php
+                        <option value="0" selected="selected">&mdash; <?php
+                            echo __('None'); ?> &mdash;</option><?php
                         if ($users=Staff::getAvailableStaffMembers()) { ?>
-                            <optgroup label="Staff Members (<?php echo count($users); ?>)">
+                            <optgroup label="<?php
+                                echo sprintf(__('Agents (%d)'), count($users)); ?>">
 <?php                       foreach($users as $id => $name) {
                                 $k = "s$id";
                                 echo sprintf('<option value="%s" %s>%s</option>',
@@ -60,7 +63,7 @@ if ($ticket && $ticket->getOwnerId() == $user->getId())
                         }
 
                         if ($teams=Team::getActiveTeams()) { ?>
-                            <optgroup label="Teams (<?php echo count($teams); ?>)">
+                            <optgroup label="<?php echo sprintf(__('Teams (%d)'), count($teams)); ?>">
 <?php                       foreach($teams as $id => $name) {
                                 $k="t$id";
                                 echo sprintf('<option value="%s" %s>%s</option>',
@@ -74,15 +77,16 @@ if ($ticket && $ticket->getOwnerId() == $user->getId())
             </tr>
             <tr>
                 <td width="180">
-                    Auto-Assignment:
+                    <?php echo __('Auto-Assignment'); ?>:
                 </td>
                 <td>
                     <input type="checkbox" name="assign-am-flag" value="1" <?php echo $info['assign-am-flag']?'checked="checked"':''; ?>>
-                    Assign tickets from this organization to the <em>Account Manager</em>
+                    <?php echo __(
+                    'Assign tickets from this organization to the <em>Account Manager</em>'); ?>
             </tr>
             <tr>
                 <td width="180">
-                    Primary Contacts:
+                    <?php echo __('Primary Contacts'); ?>:
                 </td>
                 <td>
                     <select name="contacts[]" id="primary_contacts" multiple="multiple">
@@ -96,35 +100,35 @@ if ($ticket && $ticket->getOwnerId() == $user->getId())
                 </td>
             <tr>
                 <th colspan="2">
-                    Automated Collaboration:
+                    <?php echo __('Automated Collaboration'); ?>:
                 </th>
             </tr>
             <tr>
                 <td width="180">
-                    Primary Contacts:
+                    <?php echo __('Primary Contacts'); ?>:
                 </td>
                 <td>
                     <input type="checkbox" name="collab-pc-flag" value="1" <?php echo $info['collab-pc-flag']?'checked="checked"':''; ?>>
-                    Add to all tickets from this organization
+                    <?php echo __('Add to all tickets from this organization'); ?>
                 </td>
             </tr>
             <tr>
                 <td width="180">
-                    Organization Members:
+                    <?php echo __('Organization Members'); ?>:
                 </td>
                 <td>
                     <input type="checkbox" name="collab-all-flag" value="1" <?php echo $info['collab-all-flag']?'checked="checked"':''; ?>>
-                    Add to all tickets from this organization
+                    <?php echo __('Add to all tickets from this organization'); ?>
                 </td>
             </tr>
             <tr>
                 <th colspan="2">
-                    Main Domain
+                    <?php echo __('Main Domain'); ?>
                 </th>
             </tr>
             <tr>
                 <td style="width:180px">
-                    Auto Add Members From:
+                    <?php echo __('Auto Add Members From'); ?>:
                 </td>
                 <td>
                     <input type="text" size="40" maxlength="60" name="domain"
@@ -140,13 +144,13 @@ if ($ticket && $ticket->getOwnerId() == $user->getId())
 
 <hr>
 <p class="full-width">
-    <span class="buttons" style="float:left">
-        <input type="reset" value="Reset">
+    <span class="buttons pull-left">
+        <input type="reset" value="<?php echo __('Reset'); ?>">
         <input type="button" name="cancel" class="<?php
-echo $account ? 'cancel' : 'close'; ?>"  value="Cancel">
+echo $account ? 'cancel' : 'close'; ?>"  value="<?php echo __('Cancel'); ?>">
     </span>
-    <span class="buttons" style="float:right">
-        <input type="submit" value="Update Organization">
+    <span class="buttons pull-right">
+        <input type="submit" value="<?php echo __('Update Organization'); ?>">
     </span>
 </p>
 </form>
@@ -166,6 +170,6 @@ $(function() {
         $('div#org-profile').fadeIn();
         return false;
     });
-    $("#primary_contacts").multiselect({'noneSelectedText':'Select Contacts'});
+    $("#primary_contacts").multiselect({'noneSelectedText':'<?php echo __('Select Contacts'); ?>'});
 });
 </script>
diff --git a/include/staff/templates/org.tmpl.php b/include/staff/templates/org.tmpl.php
index 4331153dec194cdcab36c0c05679ef0cfc0139dd..06f82255c7736dc9db02d58f65ed5e51073f2b18 100644
--- a/include/staff/templates/org.tmpl.php
+++ b/include/staff/templates/org.tmpl.php
@@ -16,8 +16,10 @@ if ($info['error']) {
     <?php
     if ($user) { ?>
     <a class="action-button pull-right user-action" style="overflow:inherit"
-        href="#users/<?php echo $user->getId(); ?>/org/<?php echo $org->getId(); ?>" ><i class="icon-user"></i> Change</a>
-    <a class="action-button pull-right" href="orgs.php?id=<?php echo $org->getId(); ?>"><i class="icon-share"></i> Manage</a>
+        href="#users/<?php echo $user->getId(); ?>/org/<?php echo $org->getId(); ?>" ><i class="icon-user"></i>
+        <?php echo __('Change'); ?></a>
+    <a class="action-button pull-right" href="orgs.php?id=<?php echo $org->getId(); ?>"><i class="icon-share"></i>
+        <?php echo __('Manage'); ?></a>
     <?php
     } ?>
     <div><b><a href="#" id="editorg"><i class="icon-edit"></i>&nbsp;<?php
@@ -41,7 +43,8 @@ if ($info['error']) {
     <div class="faded">Last updated <b><?php echo Format::db_datetime($org->getUpdateDate()); ?> </b></div>
 </div>
 <div id="org-form" style="display:<?php echo $forms ? 'block' : 'none'; ?>;">
-<div><p id="msg_info"><i class="icon-info-sign"></i>&nbsp; Please note that updates will be reflected system-wide.</p></div>
+<div><p id="msg_info"><i class="icon-info-sign"></i>&nbsp; <?php echo __(
+'Please note that updates will be reflected system-wide.'); ?></p></div>
 <?php
 $action = $info['action'] ? $info['action'] : ('#orgs/'.$org->getId());
 if ($ticket && $ticket->getOwnerId() == $user->getId())
@@ -58,13 +61,13 @@ if ($ticket && $ticket->getOwnerId() == $user->getId())
     </table>
     <hr>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="reset" value="Reset">
+        <span class="buttons pull-left">
+            <input type="reset" value="<?php echo __('Reset'); ?>">
             <input type="button" name="cancel" class="<?php
-    echo $account ? 'cancel' : 'close'; ?>"  value="Cancel">
+            echo $account ? 'cancel' : 'close'; ?>"  value="<?php echo __('Cancel'); ?>">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="submit" value="Update Organization">
+        <span class="buttons pull-right">
+            <input type="submit" value="<?php echo __('Update Organization'); ?>">
         </span>
      </p>
 </form>
diff --git a/include/staff/templates/sequence-manage.tmpl.php b/include/staff/templates/sequence-manage.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..6827cd6267d5d5f0c8cbae54a0af44cf162c91be
--- /dev/null
+++ b/include/staff/templates/sequence-manage.tmpl.php
@@ -0,0 +1,142 @@
+<h3><i class="icon-wrench"></i> <?php echo __('Manage Sequences'); ?></i></h3>
+<b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b>
+<hr/><?php echo __(
+'Sequences are used to generate sequential numbers. Various sequences can be
+used to generate sequences for different purposes.'); ?>
+<br/>
+<br/>
+<form method="post" action="<?php echo $info['action']; ?>">
+<div id="sequences">
+<?php
+$current_list = array();
+foreach ($sequences as $e) {
+    $field = function($field, $name=false) use ($e) { ?>
+    <input class="f<?php echo $field; ?>" type="hidden" name="seq[<?php echo $e->id;
+        ?>][<?php echo $name ?: $field; ?>]" value="<?php echo $e->{$field}; ?>"/>
+<?php }; ?>
+    <div class="row-item">
+        <?php echo $field('name'); echo $field('current', 'next'); echo $field('increment'); echo $field('padding'); ?>
+        <input type="hidden" class="fdeleted" name="seq[<?php echo $e->get('id'); ?>][deleted]" value="0"/>
+        <i class="icon-sort-by-order"></i>
+        <div style="display:inline-block" class="name"> <?php echo $e->getName(); ?> </div>
+        <div class="manage-buttons pull-right">
+            <span class="faded">next</span>
+            <span class="current"><?php echo $e->current(); ?></span>
+        </div>
+        <div class="button-group">
+            <div class="manage"><a href="#"><i class="icon-cog"></i></a></div>
+            <div class="delete"><?php if (!$e->hasFlag(Sequence::FLAG_INTERNAL)) { ?>
+                <a href="#"><i class="icon-trash"></i></a><?php } ?></div>
+        </div>
+        <div class="management hidden" data-id="<?php echo $e->id; ?>">
+            <table width="100%"><tbody>
+                <tr><td><label style="padding:0"><?php echo __('Increment'); ?>:
+                    <input class="-increment" type="text" size="4" value="<?php echo Format::htmlchars($e->increment); ?>"/>
+                    </label></td>
+                    <td><label style="padding:0"><?php echo __('Padding Character'); ?>:
+                    <input class="-padding" maxlength="1" type="text" size="4" value="<?php echo Format::htmlchars($e->padding); ?>"/>
+                    </label></td></tr>
+            </tbody></table>
+        </div>
+    </div>
+<?php } ?>
+</div>
+
+<div class="row-item hidden" id="template">
+    <i class="icon-sort-by-order"></i>
+    <div style="display:inline-block" class="name"> <?php echo __('New Sequence'); ?> </div>
+    <div class="manage-buttons pull-right">
+        <span class="faded">next</span>
+        <span class="next">1</span>
+    </div>
+    <div class="button-group">
+        <div class="manage"><a href="#"><i class="icon-cog"></i></a></div>
+        <div class="delete new"><a href="#"><i class="icon-trash"></i></a></div>
+    </div>
+    <div class="management hidden" data-id="<?php echo $e->id; ?>">
+        <table width="100%"><tbody>
+            <tr><td><label style="padding:0"><?php echo __('Increment'); ?>:
+                <input class="-increment" type="text" size="4" value="1"/>
+                </label></td>
+                <td><label style="padding:0"><?php echo __('Padding Character'); ?>:
+                <input class="-padding" maxlength="1" type="text" size="4" value="0"/>
+                </label></td></tr>
+        </tbody></table>
+    </div>
+</div>
+
+<hr/>
+<button onclick="javascript:
+  var id = ++$.uid, base = 'seq[new-'+id+']';
+  var clone = $('.row-item#template').clone()
+    .appendTo($('#sequences'))
+    .removeClass('hidden')
+    .append($('<input>').attr({type:'hidden',class:'fname',name:base+'[name]',value:'<?php echo __('New Sequence'); ?>'}))
+    .append($('<input>').attr({type:'hidden',class:'fcurrent',name:base+'[current]',value:'1'}))
+    .append($('<input>').attr({type:'hidden',class:'fincrement',name:base+'[increment]',value:'1'}))
+    .append($('<input>').attr({type:'hidden',class:'fpadding',name:base+'[padding]',value:'0'})) ;
+  clone.find('.manage a').trigger('click');
+  return false;
+  "><i class="icon-plus"></i> <?php echo __('Add New Sequence'); ?></button>
+<div id="delete-warning" style="display:none">
+<hr>
+    <div id="msg_warning"><?php echo __(
+    'Clicking <strong>Save Changes</strong> will permanently remove the
+    deleted sequences.'); ?>
+    </div>
+</div>
+<hr>
+<div>
+    <span class="buttons pull-right">
+        <input type="submit" value="<?php echo __('Save Changes'); ?>" onclick="javascript:
+$('#sequences .save a').each(function() { $(this).trigger('click'); });
+">
+    </span>
+</div>
+
+<script type="text/javascript">
+$(function() {
+  var remove = function() {
+    if (!$(this).parent().hasClass('new')) {
+      $('#delete-warning').show();
+      $(this).closest('.row-item').hide()
+        .find('input.fdeleted').val('1');
+      }
+    else
+      $(this).closest('.row-item').remove();
+    return false;
+  }, manage = function() {
+    var top = $(this).closest('.row-item');
+    top.find('.management').show(200);
+    top.find('.name').empty().append($('<input class="-name" type="text" size="40">')
+      .val(top.find('input.fname').val())
+    );
+    top.find('.current').empty().append($('<input class="-current" type="text" size="10">')
+      .val(top.find('input.fcurrent').val())
+    );
+    $(this).find('i').attr('class','icon-save');
+    $(this).parent().attr('class','save');
+    return false;
+  }, save = function() {
+    var top = $(this).closest('.row-item');
+    top.find('.management').hide(200);
+     $.each(['name', 'current'], function(i, t) {
+      var val = top.find('input.-'+t).val();
+      top.find('.'+t).empty().text(val);
+      top.find('input.f'+t).val(val);
+    });
+    $.each(['increment', 'padding'], function(i, t) {
+      top.find('input.f'+t).val(top.find('input.-'+t).val());
+    });
+    $(this).find('i').attr('class','icon-cog');
+    $(this).parent().attr('class','manage');
+    return false;
+  };
+  $(document).on('click.seq', '#sequences .manage a', manage);
+  $(document).on('click.seq', '#sequences .save a', save);
+  $(document).on('click.seq', '#sequences .delete a', remove);
+  $('.close, input:submit').click(function() {
+      $(document).die('click.seq');
+  });
+});
+</script>
diff --git a/include/staff/templates/status-options.tmpl.php b/include/staff/templates/status-options.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..edfdf19564e0367a1ebaa97e3adc1e08d075cf04
--- /dev/null
+++ b/include/staff/templates/status-options.tmpl.php
@@ -0,0 +1,63 @@
+<?php
+global $thisstaff, $ticket;
+// Map states to actions
+$actions= array(
+        'closed' => array(
+            'icon'  => 'icon-ok-circle',
+            'action' => 'close',
+            'href' => 'tickets.php'
+            ),
+        'open' => array(
+            'icon'  => 'icon-undo',
+            'action' => 'reopen'
+            ),
+        );
+?>
+
+<span
+    class="action-button pull-right"
+    data-dropdown="#action-dropdown-statuses">
+    <i class="icon-caret-down pull-right"></i>
+    <a class="tickets-action"
+        href="#statuses"><i
+        class="icon-flag"></i> <?php
+        echo __('Change Status'); ?></a>
+</span>
+<div id="action-dropdown-statuses"
+    class="action-dropdown anchor-right">
+    <ul>
+    <?php
+    $states = array('open');
+    if ($thisstaff->canCloseTickets())
+        $states = array_merge($states, array('closed'));
+
+    $statusId = $ticket ? $ticket->getStatusId() : 0;
+    foreach (TicketStatusList::getStatuses(
+                array('states' => $states))->all() as $status) {
+        if (!isset($actions[$status->getState()])
+                || $statusId == $status->getId())
+            continue;
+        ?>
+        <li>
+            <a class="no-pjax <?php
+                echo $ticket? 'ticket-action' : 'tickets-action'; ?>"
+                href="<?php
+                    echo sprintf('#%s/status/%s/%d',
+                            $ticket ? ('tickets/'.$ticket->getId()) : 'tickets',
+                            $actions[$status->getState()]['action'],
+                            $status->getId()); ?>"
+                <?php
+                if (isset($actions[$status->getState()]['href']))
+                    echo sprintf('data-href="%s"',
+                            $actions[$status->getState()]['href']);
+
+                ?>
+                ><i class="<?php
+                        echo $actions[$status->getState()]['icon'] ?: 'icon-tag';
+                    ?>"></i> <?php
+                        echo __($status->getName()); ?></a>
+        </li>
+    <?php
+    } ?>
+    </ul>
+</div>
diff --git a/include/staff/templates/ticket-preview.tmpl.php b/include/staff/templates/ticket-preview.tmpl.php
index 8e0fc32b68647b851e8bf798f388a74d2e7c0b17..92d120563de7baa0d55412223407b5d9d773e55d 100644
--- a/include/staff/templates/ticket-preview.tmpl.php
+++ b/include/staff/templates/ticket-preview.tmpl.php
@@ -9,13 +9,14 @@ $lock=$ticket->getLock();
 $error=$msg=$warn=null;
 
 if($lock && $lock->getStaffId()==$thisstaff->getId())
-    $warn.='&nbsp;<span class="Icon lockedTicket">Ticket is locked by '.$lock->getStaffName().'</span>';
+    $warn.='&nbsp;<span class="Icon lockedTicket">'
+    .sprintf(__('Ticket is locked by %s'), $lock->getStaffName()).'</span>';
 elseif($ticket->isOverdue())
-    $warn.='&nbsp;<span class="Icon overdueTicket">Marked overdue!</span>';
+    $warn.='&nbsp;<span class="Icon overdueTicket">'.__('Marked overdue!').'</span>';
 
 echo sprintf(
         '<div style="width:600px; padding: 2px 2px 0 5px;" id="t%s">
-         <h2>Ticket #%s: %s</h2><br>',
+         <h2>'.__('Ticket #%s').': %s</h2><br>',
          $ticket->getNumber(),
          $ticket->getNumber(),
          Format::htmlchars($ticket->getSubject()));
@@ -31,12 +32,12 @@ echo '<ul class="tabs">';
 
 echo '
         <li><a id="preview_tab" href="#preview" class="active"
-            ><i class="icon-list-alt"></i>&nbsp;Ticket Summary</a></li>';
+            ><i class="icon-list-alt"></i>&nbsp;'.__('Ticket Summary').'</a></li>';
 if ($ticket->getNumCollaborators()) {
 echo sprintf('
         <li><a id="collab_tab" href="#collab"
             ><i class="icon-fixed-width icon-group
-            faded"></i>&nbsp;Collaborators (%d)</a></li>',
+            faded"></i>&nbsp;'.__('Collaborators (%d)').'</a></li>',
             $ticket->getNumCollaborators());
 }
 echo '</ul>';
@@ -47,25 +48,25 @@ echo '<table border="0" cellspacing="" cellpadding="1" width="100%" class="ticke
 $ticket_state=sprintf('<span>%s</span>',ucfirst($ticket->getStatus()));
 if($ticket->isOpen()) {
     if($ticket->isOverdue())
-        $ticket_state.=' &mdash; <span>Overdue</span>';
+        $ticket_state.=' &mdash; <span>'.__('Overdue').'</span>';
     else
         $ticket_state.=sprintf(' &mdash; <span>%s</span>',$ticket->getPriority());
 }
 
 echo sprintf('
         <tr>
-            <th width="100">Ticket State:</th>
+            <th width="100">'.__('Ticket State').':</th>
             <td>%s</td>
         </tr>
         <tr>
-            <th>Create Date:</th>
+            <th>'.__('Created').':</th>
             <td>%s</td>
         </tr>',$ticket_state,
         Format::db_datetime($ticket->getCreateDate()));
 if($ticket->isClosed()) {
     echo sprintf('
             <tr>
-                <th>Close Date:</th>
+                <th>'.__('Closed').':</th>
                 <td>%s   <span class="faded">by %s</span></td>
             </tr>',
             Format::db_datetime($ticket->getCloseDate()),
@@ -74,7 +75,7 @@ if($ticket->isClosed()) {
 } elseif($ticket->getEstDueDate()) {
     echo sprintf('
             <tr>
-                <th>Due Date:</th>
+                <th>'.__('Due Date').':</th>
                 <td>%s</td>
             </tr>',
             Format::db_datetime($ticket->getEstDueDate()));
@@ -87,22 +88,22 @@ echo '<hr>
 if($ticket->isOpen()) {
     echo sprintf('
             <tr>
-                <th width="100">Assigned To:</th>
+                <th width="100">'.__('Assigned To').':</th>
                 <td>%s</td>
-            </tr>',$ticket->isAssigned()?implode('/', $ticket->getAssignees()):' <span class="faded">&mdash; Unassigned &mdash;</span>');
+            </tr>',$ticket->isAssigned()?implode('/', $ticket->getAssignees()):' <span class="faded">&mdash; '.__('Unassigned').' &mdash;</span>');
 }
 echo sprintf(
     '
         <tr>
-            <th>From:</th>
+            <th>'.__('From').':</th>
             <td><a href="users.php?id=%d" class="no-pjax">%s</a> <span class="faded">%s</span></td>
         </tr>
         <tr>
-            <th width="100">Department:</th>
+            <th width="100">'.__('Department').':</th>
             <td>%s</td>
         </tr>
         <tr>
-            <th>Help Topic:</th>
+            <th>'.__('Help Topic').':</th>
             <td>%s</td>
         </tr>',
     $ticket->getUserId(),
@@ -131,7 +132,7 @@ echo '</div>'; // ticket preview content.
                         $collab->getEmail());
             }
         }  else {
-            echo "Ticket doesn't have collaborators.";
+            echo __("Ticket doesn't have any collaborators.");
         }?>
     </table>
     <br>
@@ -140,21 +141,21 @@ echo '</div>'; // ticket preview content.
                             href="#tickets/%d/collaborators">%s</a></span>',
                             $ticket->getId(),
                             $ticket->getNumCollaborators()
-                                ? 'Manage Collaborators' : 'Add Collaborator'
+                                ? __('Manage Collaborators') : __('Add Collaborator')
                                 );
     ?>
 </div>
 <?php
 $options = array();
-$options[]=array('action'=>'Thread ('.$ticket->getThreadCount().')','url'=>"tickets.php?id=$tid");
+$options[]=array('action'=>sprintf(__('Thread (%d)'),$ticket->getThreadCount()),'url'=>"tickets.php?id=$tid");
 if($ticket->getNumNotes())
-    $options[]=array('action'=>'Notes ('.$ticket->getNumNotes().')','url'=>"tickets.php?id=$tid#notes");
+    $options[]=array('action'=>sprintf(__('Notes (%d)'),$ticket->getNumNotes()),'url'=>"tickets.php?id=$tid#notes");
 
 if($ticket->isOpen())
-    $options[]=array('action'=>'Reply','url'=>"tickets.php?id=$tid#reply");
+    $options[]=array('action'=>__('Reply'),'url'=>"tickets.php?id=$tid#reply");
 
 if($thisstaff->canAssignTickets())
-    $options[]=array('action'=>($ticket->isAssigned()?'Reassign':'Assign'),'url'=>"tickets.php?id=$tid#assign");
+    $options[]=array('action'=>($ticket->isAssigned()?__('Reassign'):__('Assign')),'url'=>"tickets.php?id=$tid#assign");
 
 if($thisstaff->canTransferTickets())
     $options[]=array('action'=>'Transfer','url'=>"tickets.php?id=$tid#transfer");
diff --git a/include/staff/templates/ticket-status.tmpl.php b/include/staff/templates/ticket-status.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..c033e0caa8d541b2a9bbed596fb3e8ba2d8c7c80
--- /dev/null
+++ b/include/staff/templates/ticket-status.tmpl.php
@@ -0,0 +1,118 @@
+<?php
+global $cfg;
+
+if (!$info['title'])
+    $info['title'] = 'Change Tickets Status';
+
+?>
+<h3><?php echo $info['title']; ?></h3>
+<b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b>
+<div class="clear"></div>
+<hr/>
+<?php
+if ($info['error']) {
+    echo sprintf('<p id="msg_error">%s</p>', $info['error']);
+} elseif ($info['warn']) {
+    echo sprintf('<p id="msg_warning">%s</p>', $info['warn']);
+} elseif ($info['msg']) {
+    echo sprintf('<p id="msg_notice">%s</p>', $info['msg']);
+} elseif ($info['notice']) {
+   echo sprintf('<p id="msg_info"><i class="icon-info-sign"></i> %s</p>',
+           $info['notice']);
+}
+
+
+$action = $info['action'] ?: ('#tickets/status/'. $state);
+?>
+<div id="ticket-status" style="display:block; margin:5px;">
+    <form method="post" name="status" id="status"
+        action="<?php echo $action; ?>">
+        <table width="100%">
+            <?php
+            if ($info['extra']) {
+                ?>
+            <tbody>
+                <tr><td colspan="2"><strong><?php echo $info['extra'];
+                ?></strong></td> </tr>
+            </tbody>
+            <?php
+            }
+
+            $verb = '';
+            if ($state) {
+                $statuses = TicketStatusList::getStatuses(array('states'=>array($state)))->all();
+                $verb = TicketStateField::getVerb($state);
+            }
+
+            if ($statuses) {
+            ?>
+            <tbody>
+                <tr>
+                    <td colspan=2>
+                        <span>
+                        <?php
+                        if (count($statuses) > 1) { ?>
+                            <strong><?php echo __('Status') ?>:&nbsp;</strong>
+                            <select name="status_id">
+                            <?php
+                            foreach ($statuses as $s) {
+                                echo sprintf('<option value="%d" %s>%s</option>',
+                                        $s->getId(),
+                                        ($info['status_id'] == $s->getId())
+                                         ? 'selected="selected"' : '',
+                                        $s->getName()
+                                        );
+                            }
+                            ?>
+                            </select>
+                            <font class="error">*&nbsp;<?php echo $errors['status_id']; ?></font>
+                        <?php
+                        } elseif ($statuses[0]) {
+                            echo  "<input type='hidden' name='status_id' value={$statuses[0]->getId()} />";
+                        } ?>
+                        </span>
+                    </td>
+                </tr>
+            </tbody>
+            <?php
+            } ?>
+            <tbody>
+                <tr>
+                    <td colspan="2">
+                        <?php
+                        $placeholder = $info['placeholder'] ?: __('Optional reason for status change (internal note)');
+                        ?>
+                        <textarea name="comments" id="comments"
+                            cols="50" rows="3" wrap="soft" style="width:100%"
+                            class="richtext ifhtml no-bar"
+                            placeholder="<?php echo $placeholder; ?>"><?php
+                            echo $info['comments']; ?></textarea>
+                    </td>
+                </tr>
+            </tbody>
+        </table>
+        <hr>
+        <p class="full-width">
+            <span class="buttons pull-left">
+                <input type="reset" value="<?php echo __('Reset'); ?>">
+                <input type="button" name="cancel" class="close"
+                value="<?php echo __('Cancel'); ?>">
+            </span>
+            <span class="buttons pull-right">
+                <input type="submit" value="<?php
+                echo $verb ?: __('Submit'); ?>">
+            </span>
+         </p>
+    </form>
+</div>
+<div class="clear"></div>
+<script type="text/javascript">
+$(function() {
+    // Copy checked tickets to status form.
+    $('form#tickets input[name="tids[]"]:checkbox:checked')
+    .clone()
+    .prop('type', 'hidden')
+    .removeAttr('class')
+    .appendTo('form#status');
+ });
+</script>
diff --git a/include/staff/templates/tickets.tmpl.php b/include/staff/templates/tickets.tmpl.php
index abf3314ef753f97742d97d7a2398a8f9ef73d919..ea0aae0b7e3b28be02252f2d245100b4b73cbddc 100644
--- a/include/staff/templates/tickets.tmpl.php
+++ b/include/staff/templates/tickets.tmpl.php
@@ -1,14 +1,16 @@
 <?php
 
 $select ='SELECT ticket.ticket_id,ticket.`number`,ticket.dept_id,ticket.staff_id,ticket.team_id, ticket.user_id '
-        .' ,dept.dept_name,ticket.status,ticket.source,ticket.isoverdue,ticket.isanswered,ticket.created '
+        .' ,dept.dept_name,status.name as status,ticket.source,ticket.isoverdue,ticket.isanswered,ticket.created '
         .' ,CAST(GREATEST(IFNULL(ticket.lastmessage, 0), IFNULL(ticket.reopened, 0), ticket.created) as datetime) as effective_date '
         .' ,CONCAT_WS(" ", staff.firstname, staff.lastname) as staff, team.name as team '
         .' ,IF(staff.staff_id IS NULL,team.name,CONCAT_WS(" ", staff.lastname, staff.firstname)) as assigned '
         .' ,IF(ptopic.topic_pid IS NULL, topic.topic, CONCAT_WS(" / ", ptopic.topic, topic.topic)) as helptopic '
-        .' ,cdata.priority_id, cdata.subject, user.name, email.address as email';
+        .' ,cdata.priority as priority_id, cdata.subject, user.name, email.address as email';
 
 $from =' FROM '.TICKET_TABLE.' ticket '
+      .' LEFT JOIN '.TICKET_STATUS_TABLE.' status
+        ON status.id = ticket.status_id '
       .' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id '
       .' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id '
       .' LEFT JOIN '.USER_ACCOUNT_TABLE.' account ON (ticket.user_id=account.user_id) '
@@ -18,7 +20,7 @@ $from =' FROM '.TICKET_TABLE.' ticket '
       .' LEFT JOIN '.TOPIC_TABLE.' topic ON (ticket.topic_id=topic.topic_id) '
       .' LEFT JOIN '.TOPIC_TABLE.' ptopic ON (ptopic.topic_id=topic.topic_pid) '
       .' LEFT JOIN '.TABLE_PREFIX.'ticket__cdata cdata ON (cdata.ticket_id = ticket.ticket_id) '
-      .' LEFT JOIN '.PRIORITY_TABLE.' pri ON (pri.priority_id = cdata.priority_id)';
+      .' LEFT JOIN '.PRIORITY_TABLE.' pri ON (pri.priority_id = cdata.priority)';
 
 if ($user)
     $where = 'WHERE ticket.user_id = '.db_input($user->getId());
@@ -54,19 +56,21 @@ if ($results) {
     }
 }
 ?>
-<div style="width:700px; float:left;">
+<div style="width:700px;" class="pull-left">
    <?php
     if ($results) {
-        echo  sprintf('<strong>Showing 1 - %d of %s</strong>', count($results), count($results));
+        echo '<strong>'.sprintf(_N('Showing %d ticket', 'Showing %d tickets',
+            count($results)), count($results)).'</strong>';
     } else {
-        echo sprintf('%s does not have any tickets', $user? 'User' : 'Organization');
+        echo sprintf(__('%s does not have any tickets'), $user? 'User' : 'Organization');
     }
    ?>
 </div>
-<div style="float:right;text-align:right;padding-right:5px;">
+<div class="pull-right flush-right" style="padding-right:5px;">
     <?php
     if ($user) { ?>
-    <b><a class="Icon newTicket" href="tickets.php?a=open&uid=<?php echo $user->getId(); ?>"> Create New Ticket</a></b>
+    <b><a class="Icon newTicket" href="tickets.php?a=open&uid=<?php echo $user->getId(); ?>">
+    <?php print __('Create New Ticket'); ?></a></b>
     <?php
     } ?>
 </div>
@@ -86,17 +90,17 @@ if ($results) { ?>
             <th width="8px">&nbsp;</th>
             <?php
             } ?>
-            <th width="70">Ticket</th>
-            <th width="100">Date</th>
-            <th width="100">Status</th>
-            <th width="300">Subject</th>
+            <th width="70"><?php echo __('Ticket'); ?></th>
+            <th width="100"><?php echo __('Date'); ?></th>
+            <th width="100"><?php echo __('Status'); ?></th>
+            <th width="300"><?php echo __('Subject'); ?></th>
             <?php
             if ($user) { ?>
-            <th width="200">Department</th>
-            <th width="200">Assignee</th>
+            <th width="200"><?php echo __('Department'); ?></th>
+            <th width="200"><?php echo __('Assignee'); ?></th>
             <?php
             } else { ?>
-            <th width="400">User</th>
+            <th width="400"><?php echo __('User'); ?></th>
             <?php
             } ?>
         </tr>
@@ -119,9 +123,6 @@ if ($results) { ?>
             $assigned=' ';
 
         $status = ucfirst($row['status']);
-        if(!strcasecmp($row['status'], 'open'))
-            $status = "<b>$status</b>";
-
         $tid=$row['number'];
         $subject = Format::htmlchars(Format::truncate($row['subject'],40));
         $threadcount=$row['thread_count'];
@@ -136,7 +137,8 @@ if ($results) { ?>
             <?php
             } ?>
             <td align="center" nowrap>
-              <a class="Icon <?php echo strtolower($row['source']); ?>Ticket ticketPreview" title="Preview Ticket"
+              <a class="Icon <?php echo strtolower($row['source']); ?>Ticket ticketPreview"
+                title="<?php echo __('Preview Ticket'); ?>"
                 href="tickets.php?id=<?php echo $row['ticket_id']; ?>"><?php echo $tid; ?></a></td>
             <td align="center" nowrap><?php echo Format::db_datetime($row['effective_date']); ?></td>
             <td><?php echo $status; ?></td>
diff --git a/include/staff/templates/user-account.tmpl.php b/include/staff/templates/user-account.tmpl.php
index 1ed07b9ea2b4cf76f80da57de136e6a2212c5fec..e68261a29174f86f190826f672713e02aa72b012 100644
--- a/include/staff/templates/user-account.tmpl.php
+++ b/include/staff/templates/user-account.tmpl.php
@@ -17,9 +17,9 @@ if ($info['error']) {
 } ?>
 <ul class="tabs">
     <li><a href="#user-account" <?php echo !$access? 'class="active"' : ''; ?>
-        ><i class="icon-user"></i>&nbsp;User Information</a></li>
+        ><i class="icon-user"></i>&nbsp;<?php echo __('User Information'); ?></a></li>
     <li><a href="#user-access" <?php echo $access? 'class="active"' : ''; ?>
-        ><i class="icon-fixed-width icon-lock faded"></i>&nbsp;Manage Access</a></li>
+        ><i class="icon-fixed-width icon-lock faded"></i>&nbsp;<?php echo __('Manage Access'); ?></a></li>
 </ul>
 
 
@@ -32,24 +32,24 @@ if ($info['error']) {
         <tbody>
             <tr>
                 <th colspan="2">
-                    <em><strong>User Information</strong></em>
+                    <em><strong><?php echo __('User Information'); ?></strong></em>
                 </th>
             </tr>
             <tr>
                 <td width="180">
-                    Name:
+                    <?php echo __('Name'); ?>:
                 </td>
                 <td> <?php echo Format::htmlchars($user->getName()); ?> </td>
             </tr>
             <tr>
                 <td width="180">
-                    Email:
+                    <?php echo __('Email'); ?>:
                 </td>
                 <td> <?php echo $user->getEmail(); ?> </td>
             </tr>
             <tr>
                 <td width="180">
-                    Organization:
+                    <?php echo __('Organization'); ?>:
                 </td>
                 <td>
                     <input type="text" size="35" name="org" value="<?php echo $info['org']; ?>">
@@ -59,9 +59,9 @@ if ($info['error']) {
         </tbody>
         <tbody>
             <tr>
-                <th colspan="2"><em><strong>User Preferences</strong></em></th>
+                <th colspan="2"><em><strong><?php echo __('User Preferences'); ?></strong></em></th>
             </tr>
-                <td>Time Zone:</td>
+                <td><?php echo __('Time Zone'); ?>:</td>
                 <td>
                     <select name="timezone_id" id="timezone_id">
                         <?php
@@ -79,11 +79,11 @@ if ($info['error']) {
             </tr>
             <tr>
                 <td width="180">
-                   Daylight Saving:
+                   <?php echo __('Daylight Saving'); ?>:
                 </td>
                 <td>
                     <input type="checkbox" name="dst" value="1" <?php echo $info['dst']?'checked="checked"':''; ?>>
-                    Observe daylight saving
+                    <?php echo __('Observe daylight saving'); ?>
                 </td>
             </tr>
         </tbody>
@@ -93,24 +93,29 @@ if ($info['error']) {
         <table width="100%">
         <tbody>
             <tr>
-                <th colspan="2"><em><strong>Account Access</strong></em></th>
+                <th colspan="2"><em><strong><?php echo __('Account Access'); ?></strong></em></th>
             </tr>
             <tr>
-                <td width="180"> Status: </td>
+                <td width="180"><?php echo __('Status'); ?>:</td>
                 <td> <?php echo $user->getAccountStatus(); ?> </td>
             </tr>
             <tr>
                 <td width="180">
-                    Username:
+                    <?php echo __('Username'); ?>:
                 </td>
                 <td>
-                    <input type="text" size="35" name="username" value="<?php echo $info['username'] ?: $user->getEmail(); ?>">
-                    &nbsp;<span class="error">&nbsp;<?php echo $errors['username']; ?></span>
+                    <input type="text" size="35" name="username" value="<?php echo $info['username']; ?>">
+                    <i class="help-tip icon-question-sign" data-title="<?php
+                        echo __("Login via email"); ?>"
+                    data-content="<?php echo sprintf('%s: %s',
+                        __('Users can always sign in with their email address'),
+                        $user->getEmail()); ?>"></i>
+                    <div class="error">&nbsp;<?php echo $errors['username']; ?></div>
                 </td>
             </tr>
             <tr>
                 <td width="180">
-                    New Password:
+                    <?php echo __('New Password'); ?>:
                 </td>
                 <td>
                     <input type="password" size="35" name="passwd1" value="<?php echo $info['passwd1']; ?>">
@@ -120,7 +125,7 @@ if ($info['error']) {
             </tr>
             <tr>
                 <td width="180">
-                   Confirm Password:
+                   <?php echo __('Confirm Password'); ?>:
                 </td>
                 <td>
                     <input type="password" size="35" name="passwd2" value="<?php echo $info['passwd2']; ?>">
@@ -130,22 +135,23 @@ if ($info['error']) {
         </tbody>
         <tbody>
             <tr>
-                <th colspan="2"><em><strong>Account Flags</strong></em></th>
+                <th colspan="2"><em><strong><?php echo __('Account Flags'); ?></strong></em></th>
             </tr>
             <tr>
                 <td colspan="2">
                 <?php
                   echo sprintf('<div><input type="checkbox" name="locked-flag" %s
-                       value="1"> Administratively Locked</div>',
-                       $account->isLocked() ?  'checked="checked"' : ''
+                       value="1"> %s</div>',
+                       $account->isLocked() ?  'checked="checked"' : '',
+                       __('Administratively Locked')
                        );
                   ?>
                    <div><input type="checkbox" name="pwreset-flag" value="1" <?php
                     echo $account->isPasswdResetForced() ?
-                    'checked="checked"' : ''; ?>> Password Reset Required</div>
+                    'checked="checked"' : ''; ?>> <?php echo __('Password Reset Required'); ?></div>
                    <div><input type="checkbox" name="forbid-pwchange-flag" value="1" <?php
                     echo !$account->isPasswdResetEnabled() ?
-                    'checked="checked"' : ''; ?>> User Cannot Change Password</div>
+                    'checked="checked"' : ''; ?>> <?php echo __('User Cannot Change Password'); ?></div>
                 </td>
             </tr>
         </tbody>
@@ -153,13 +159,13 @@ if ($info['error']) {
    </div>
    <hr>
    <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="reset" value="Reset">
-            <input type="button" name="cancel" class="close" value="Cancel">
+        <span class="buttons pull-left">
+            <input type="reset" value="<?php echo __('Reset'); ?>">
+            <input type="button" name="cancel" class="close" value="<?php echo __('Cancel'); ?>">
         </span>
-        <span class="buttons" style="float:right">
+        <span class="buttons pull-right">
             <input type="submit"
-                value="Save Changes">
+                value="<?php echo __('Save Changes'); ?>">
         </span>
     </p>
 </form>
diff --git a/include/staff/templates/user-delete.tmpl.php b/include/staff/templates/user-delete.tmpl.php
index ae9b5ca1ceab456598eb8d4dfe3f47ef763751b3..b563643d284146896f33ce2df58532d6da5d4554 100644
--- a/include/staff/templates/user-delete.tmpl.php
+++ b/include/staff/templates/user-delete.tmpl.php
@@ -1,9 +1,9 @@
 <?php
 
 if (!$info['title'])
-    $info['title'] = 'Delete User: '.Format::htmlchars($user->getName());
+    $info['title'] = sprintf(__('Delete User: %s'), Format::htmlchars($user->getName()));
 
-$info['warn'] = 'Deleted users and tickets CANNOT be recovered';
+$info['warn'] = __('Deleted users and tickets CANNOT be recovered');
 
 ?>
 <h3><?php echo $info['title']; ?></h3>
@@ -26,7 +26,7 @@ if ($info['error']) {
     if (0 && $user->getNumTickets()) { ?>
     <a class="action-button pull-right change-user" style="overflow:inherit"
         href="#users/<?php echo $user->getId(); ?>/replace" ><i
-        class="icon-user"></i> Change Tickets Ownership</a>
+        class="icon-user"></i> <?php echo __('Change Tickets Ownership'); ?></a>
     <?php
     } ?>
     <div><b> <?php echo Format::htmlchars($user->getName()->getOriginal()); ?></b></div>
@@ -53,23 +53,24 @@ if ($info['error']) {
 
     <?php
     if (($num=$user->tickets->count())) {
-        echo sprintf('<div><input type="checkbox" name="deletetickets" value="1" >
-            <strong>Delete <a href="tickets.php?a=search&uid=%d" target="_blank">%d
-            %s</a> and any associated attachments and data.</strong></div><hr>',
-            $user->getId(),
-            $num,
-            ($num >1) ? 'tickets' : 'ticket'
-            );
+        echo '<div><input type="checkbox" name="deletetickets" value="1" > <strong>'
+            .sprintf(__('Delete %1$s %2$s %3$s and any associated attachments and data.'),
+                sprintf('<a href="tickets.php?a=search&uid=%d" target="_blank">',
+                    $user->getId()),
+                sprintf(_N('one ticket', '%d tickets', $num), $num),
+                '</a>'
+            )
+            .'</strong></div><hr>';
     }
     ?>
         <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="reset" value="Reset">
+        <span class="buttons pull-left">
+            <input type="reset" value="<?php echo __('Reset'); ?>">
             <input type="button" name="cancel" class="close"
-                value="No, Cancel">
+                value="<?php echo __('No, Cancel'); ?>">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="submit" value="Yes, Delete User">
+        <span class="buttons pull-right">
+            <input type="submit" value="<?php echo __('Yes, Delete User'); ?>">
         </span>
         </p>
     </form>
diff --git a/include/staff/templates/user-import.tmpl.php b/include/staff/templates/user-import.tmpl.php
index 581f1cb48b3c6e191d14c99df80b7995deb16b95..447f83ee2441b3764853e2f5e7098b800c6bc9db 100644
--- a/include/staff/templates/user-import.tmpl.php
+++ b/include/staff/templates/user-import.tmpl.php
@@ -12,9 +12,9 @@ if ($info['error']) {
 } ?>
 <ul class="tabs">
     <li><a href="#copy-paste" class="active"
-        ><i class="icon-edit"></i>&nbsp;Copy Paste</a></li>
+        ><i class="icon-edit"></i>&nbsp;<?php echo __('Copy Paste'); ?></a></li>
     <li><a href="#upload"
-        ><i class="icon-fixed-width icon-cloud-upload"></i>&nbsp;Upload</a></li>
+        ><i class="icon-fixed-width icon-cloud-upload"></i>&nbsp;<?php echo __('Upload'); ?></a></li>
 </ul>
 <form action="<?php echo $info['action']; ?>" method="post" enctype="multipart/form-data"
     onsubmit="javascript:
@@ -28,30 +28,30 @@ if ($org_id) { ?>
 <?php } ?>
 
 <div class="tab_content" id="copy-paste" style="margin:5px;">
-<h2 style="margin-bottom:10px">Name and Email</h2>
-<p>
-Enter one name and email address per line.<br/>
-<em>To import more other fields, use the Upload tab.</em>
+<h2 style="margin-bottom:10px"><?php echo __('Name and Email'); ?></h2>
+<p><?php echo __(
+'Enter one name and email address per line.'); ?><br/><em><?php echo __(
+'To import more other fields, use the Upload tab.'); ?></em>
 </p>
 <textarea name="pasted" style="display:block;width:100%;height:8em"
-    placeholder="e.g. John Doe, john.doe@osticket.com">
+    placeholder="<?php echo __('e.g. John Doe, john.doe@osticket.com'); ?>">
 <?php echo $info['pasted']; ?>
 </textarea>
 </div>
 
 <div class="tab_content" id="upload" style="display:none;margin:5px;">
-<h2 style="margin-bottom:10px">Import a CSV File</h2>
+<h2 style="margin-bottom:10px"><?php echo __('Import a CSV File'); ?></h2>
 <p>
-<em>Use the columns shown in the table below. To add more fields, visit the
-Admin Panel -&gt; Manage -&gt; Forms -&gt; <?php echo
-UserForm::getUserForm()->get('title'); ?> page to edit the available fields.
-Only fields with `variable` defined can be imported.</em>
+<em><?php echo sprintf(__(
+'Use the columns shown in the table below. To add more fields, visit the Admin Panel -&gt; Manage -&gt; Forms -&gt; %s page to edit the available fields.  Only fields with `variable` defined can be imported.'),
+    UserForm::getUserForm()->get('title')
+); ?>
 </p>
 <table class="list"><tr>
 <?php
     $fields = array();
     $data = array(
-        array('name' => 'John Doe', 'email' => 'john.doe@osticket.com')
+        array('name' => __('John Doe'), 'email' => __('john.doe@osticket.com'))
     );
     foreach (UserForm::getUserForm()->getFields() as $f)
         if ($f->get('name'))
@@ -74,12 +74,13 @@ Only fields with `variable` defined can be imported.</em>
 </div>
     <hr>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="reset" value="Reset">
-            <input type="button" name="cancel" class="close"  value="Cancel">
+        <span class="buttons pull-left">
+            <input type="reset" value="<?php echo __('Reset'); ?>">
+            <input type="button" name="cancel" class="close"  value="<?php
+            echo __('Cancel'); ?>">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="submit" value="Import Users">
+        <span class="buttons pull-right">
+            <input type="submit" value="<?php echo __('Import Users'); ?>">
         </span>
      </p>
 </form>
diff --git a/include/staff/templates/user-lookup.tmpl.php b/include/staff/templates/user-lookup.tmpl.php
index d21d457d2d1c6fe1bb7d6236bfdad276998111a8..fa18f885888041d997c5e75e192d5644e7fd35b2 100644
--- a/include/staff/templates/user-lookup.tmpl.php
+++ b/include/staff/templates/user-lookup.tmpl.php
@@ -4,10 +4,12 @@
 <hr/>
 <?php
 if (!isset($info['lookup']) || $info['lookup'] !== false) { ?>
-<div><p id="msg_info"><i class="icon-info-sign"></i>&nbsp; Search existing users or add a new user.</p></div>
+<div><p id="msg_info"><i class="icon-info-sign"></i>&nbsp; <?php echo __(
+'Search existing users or add a new user.'
+); ?></p></div>
 <div style="margin-bottom:10px;">
     <input type="text" class="search-input" style="width:100%;"
-    placeholder="Search by email, phone or name" id="user-search"
+    placeholder="<?php echo __('Search by email, phone or name'); ?>" id="user-search"
     autocorrect="off" autocomplete="off"/>
 </div>
 <?php
@@ -25,7 +27,8 @@ if ($info['error']) {
     <input type="hidden" id="user-id" name="id" value="<?php echo $user ? $user->getId() : 0; ?>"/>
     <i class="icon-user icon-4x pull-left icon-border"></i>
     <a class="action-button pull-right" style="overflow:inherit"
-        id="unselect-user"  href="#"><i class="icon-remove"></i> Add New User</a>
+        id="unselect-user"  href="#"><i class="icon-remove"></i>
+        <?php echo __('Add New User'); ?></a>
 <?php if ($user) { ?>
     <div><strong id="user-name"><?php echo Format::htmlchars($user->getName()->getOriginal()); ?></strong></div>
     <div>&lt;<span id="user-email"><?php echo $user->getEmail(); ?></span>&gt;</div>
@@ -51,11 +54,12 @@ if ($info['error']) {
     <div class="clear"></div>
     <hr>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" name="cancel" class="close"  value="Cancel">
+        <span class="buttons pull-left">
+            <input type="button" name="cancel" class="close"  value="<?php
+            echo __('Cancel'); ?>">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="submit" value="Continue">
+        <span class="buttons pull-right">
+            <input type="submit" value="<?php echo __('Continue'); ?>">
         </span>
      </p>
 </form>
@@ -65,16 +69,16 @@ if ($info['error']) {
     <table width="100%" class="fixed">
     <?php
         if(!$form) $form = UserForm::getInstance();
-        $form->render(true, 'Create New User'); ?>
+        $form->render(true, __('Create New User')); ?>
     </table>
     <hr>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="reset" value="Reset">
-            <input type="button" name="cancel" class="<?php echo $user ? 'cancel' : 'close' ?>"  value="Cancel">
+        <span class="buttons pull-left">
+            <input type="reset" value="<?php echo __('Reset'); ?>">
+            <input type="button" name="cancel" class="<?php echo $user ?  'cancel' : 'close' ?>"  value="<?php echo __('Cancel'); ?>">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="submit" value="Add User">
+        <span class="buttons pull-right">
+            <input type="submit" value="<?php echo __('Add User'); ?>">
         </span>
      </p>
 </form>
diff --git a/include/staff/templates/user-register.tmpl.php b/include/staff/templates/user-register.tmpl.php
index f0c01449c58651aab8d1fc92e4c1df630f68675e..015f82edf9fa5a66fe9d0c744deeb8a8eb665896 100644
--- a/include/staff/templates/user-register.tmpl.php
+++ b/include/staff/templates/user-register.tmpl.php
@@ -2,7 +2,7 @@
 global $cfg;
 
 if (!$info['title'])
-    $info['title'] = 'Register: '.Format::htmlchars($user->getName());
+    $info['title'] = sprintf(__('Register: %s'), Format::htmlchars($user->getName()));
 
 if (!$_POST) {
 
@@ -26,9 +26,12 @@ if ($info['error']) {
 } elseif ($info['msg']) {
     echo sprintf('<p id="msg_notice">%s</p>', $info['msg']);
 } ?>
-<div><p id="msg_info"><i class="icon-info-sign"></i>&nbsp;Complete the form
-below to create a user account for <b><?php echo
-Format::htmlchars($user->getName()->getOriginal()); ?></b>.</p></div>
+<div><p id="msg_info"><i class="icon-info-sign"></i>&nbsp;<?php
+echo sprintf(__(
+'Complete the form below to create a user account for <b>%s</b>.'
+), Format::htmlchars($user->getName()->getOriginal())
+); ?>
+</p></div>
 <div id="user-registration" style="display:block; margin:5px;">
     <form method="post" class="user"
         action="#users/<?php echo $user->getId(); ?>/register">
@@ -37,11 +40,11 @@ Format::htmlchars($user->getName()->getOriginal()); ?></b>.</p></div>
         <tbody>
             <tr>
                 <th colspan="2">
-                    <em><strong>User Account Login</strong></em>
+                    <em><strong><?php echo __('User Account Login'); ?></strong></em>
                 </th>
             </tr>
             <tr>
-                <td>Authentication Sources:</td>
+                <td><?php echo __('Authentication Sources'); ?>:</td>
                 <td>
             <select name="backend" id="backend-selection" onchange="javascript:
                 if (this.value != '' && this.value != 'client') {
@@ -56,20 +59,20 @@ Format::htmlchars($user->getName()->getOriginal()); ?></b>.</p></div>
                         $('#password').show();
                 }
                 ">
-                <option value="">&mdash; Use any available backend &mdash;</option>
+                <option value="">&mdash; <?php echo __('Use any available backend'); ?> &mdash;</option>
             <?php foreach (UserAuthenticationBackend::allRegistered() as $ab) {
                 if (!$ab->supportsInteractiveAuthentication()) continue; ?>
                 <option value="<?php echo $ab::$id; ?>" <?php
                     if ($info['backend'] == $ab::$id)
                         echo 'selected="selected"'; ?>><?php
-                    echo $ab::$name; ?></option>
+                    echo $ab->getName(); ?></option>
             <?php } ?>
             </select>
                 </td>
             </tr>
             <tr>
                 <td width="180">
-                    Username:
+                    <?php echo __('Username'); ?>:
                 </td>
                 <td>
                     <input type="text" size="35" name="username" value="<?php echo $info['username'] ?: $user->getEmail(); ?>">
@@ -80,12 +83,13 @@ Format::htmlchars($user->getName()->getOriginal()); ?></b>.</p></div>
         <tbody id="activation">
             <tr>
                 <td width="180">
-                    Status:
+                    <?php echo __('Status'); ?>:
                 </td>
                 <td>
                   <input type="checkbox" id="sendemail" name="sendemail" value="1"
-                    <?php echo $info['sendemail'] ? 'checked="checked"' : ''; ?> >
-                    Send account activation email to <?php echo $user->getEmail(); ?>.
+                    <?php echo $info['sendemail'] ? 'checked="checked"' :
+                    ''; ?> ><?php echo sprintf(__(
+                    'Send account activation email to %s.'), $user->getEmail()); ?>
                 </td>
             </tr>
         </tbody>
@@ -94,7 +98,7 @@ Format::htmlchars($user->getName()->getOriginal()); ?></b>.</p></div>
             >
             <tr>
                 <td width="180">
-                    Temp. Password:
+                    <?php echo __('Temporary Password'); ?>:
                 </td>
                 <td>
                     <input type="password" size="35" name="passwd1" value="<?php echo $info['passwd1']; ?>">
@@ -104,7 +108,7 @@ Format::htmlchars($user->getName()->getOriginal()); ?></b>.</p></div>
             </tr>
             <tr>
                 <td width="180">
-                   Confirm Password:
+                   <?php echo __('Confirm Password'); ?>:
                 </td>
                 <td>
                     <input type="password" size="35" name="passwd2" value="<?php echo $info['passwd2']; ?>">
@@ -113,22 +117,25 @@ Format::htmlchars($user->getName()->getOriginal()); ?></b>.</p></div>
             </tr>
             <tr>
                 <td>
-                    Password Change:
+                    <?php echo __('Password Change'); ?>:
                 </td>
                 <td colspan=2>
                     <input type="checkbox" name="pwreset-flag" value="1" <?php
-                        echo $info['pwreset-flag'] ?  'checked="checked"' : ''; ?>> Require password change on login
+                        echo $info['pwreset-flag'] ?  'checked="checked"' : ''; ?>>
+                        <?php echo __('Require password change on login'); ?>
                     <br/>
                     <input type="checkbox" name="forbid-pwreset-flag" value="1" <?php
-                        echo $info['forbid-pwreset-flag'] ?  'checked="checked"' : ''; ?>> User cannot change password
+                        echo $info['forbid-pwreset-flag'] ?  'checked="checked"' : ''; ?>>
+                        <?php echo __('User cannot change password'); ?>
                 </td>
             </tr>
         </tbody>
         <tbody>
             <tr>
-                <th colspan="2"><em><strong>User Preferences</strong></em></th>
+                <th colspan="2"><em><strong><?php echo
+                    __('User Preferences'); ?></strong></em></th>
             </tr>
-                <td>Time Zone:</td>
+                <td><?php echo __('Time Zone'); ?>:</td>
                 <td>
                     <select name="timezone_id" id="timezone_id">
                         <?php
@@ -147,23 +154,23 @@ Format::htmlchars($user->getName()->getOriginal()); ?></b>.</p></div>
             </tr>
             <tr>
                 <td width="180">
-                   Daylight Saving:
+                   <?php echo __('Daylight Saving'); ?>:
                 </td>
                 <td>
                     <input type="checkbox" name="dst" value="1" <?php echo $info['dst'] ? 'checked="checked"' : ''; ?>>
-                    Observe daylight saving
+                    <?php echo __('Observe daylight saving'); ?>
                 </td>
             </tr>
         </tbody>
         </table>
         <hr>
         <p class="full-width">
-            <span class="buttons" style="float:left">
-                <input type="reset" value="Reset">
-                <input type="button" name="cancel" class="close" value="Cancel">
+            <span class="buttons pull-left">
+                <input type="reset" value="<?php echo __('Reset'); ?>">
+                <input type="button" name="cancel" class="close" value="<?php echo __('Cancel'); ?>">
             </span>
-            <span class="buttons" style="float:right">
-                <input type="submit" value="Create Account">
+            <span class="buttons pull-right">
+                <input type="submit" value="<?php echo __('Create Account'); ?>">
             </span>
          </p>
     </form>
diff --git a/include/staff/templates/user.tmpl.php b/include/staff/templates/user.tmpl.php
index 52f5f9fc7fc44b455649cd35bdccf7354b4406ea..af21b01286708196a9d3ebfce6b4b08849f9954f 100644
--- a/include/staff/templates/user.tmpl.php
+++ b/include/staff/templates/user.tmpl.php
@@ -20,7 +20,8 @@ if ($info['error']) {
     <?php
     if ($ticket) { ?>
     <a class="action-button pull-right change-user" style="overflow:inherit"
-        href="#tickets/<?php echo $ticket->getId(); ?>/change-user" ><i class="icon-user"></i> Change User</a>
+        href="#tickets/<?php echo $ticket->getId(); ?>/change-user" ><i class="icon-user"></i>
+        <?php echo __('Change User'); ?></a>
     <?php
     } ?>
     <div><b><?php
@@ -35,22 +36,22 @@ if ($info['error']) {
 <div class="clear"></div>
 <ul class="tabs" style="margin-top:5px">
     <li><a href="#info-tab" class="active"
-        ><i class="icon-info-sign"></i>&nbsp;User</a></li>
+        ><i class="icon-info-sign"></i>&nbsp;<?php echo __('User'); ?></a></li>
 <?php if ($org) { ?>
     <li><a href="#organization-tab"
-        ><i class="icon-fixed-width icon-building"></i>&nbsp;Organization</a></li>
+        ><i class="icon-fixed-width icon-building"></i>&nbsp;<?php echo __('Organization'); ?></a></li>
 <?php }
     $ext_id = "U".$user->getId();
     $notes = QuickNote::forUser($user, $org)->all(); ?>
     <li><a href="#notes-tab"
-        ><i class="icon-fixed-width icon-pushpin"></i>&nbsp;Notes</a></li>
+        ><i class="icon-fixed-width icon-pushpin"></i>&nbsp;<?php echo __('Notes'); ?></a></li>
 </ul>
 
 <div class="tab_content" id="info-tab">
 <div class="floating-options">
-    <a href="<?php echo $info['useredit'] ?: '#'; ?>" id="edituser" class="action" title="Edit"><i class="icon-edit"></i></a>
-    <a href="users.php?id=<?php echo $user->getId(); ?>" title="Manage User"
-        class="action"><i class="icon-share"></i></a>
+    <a href="<?php echo $info['useredit'] ?: '#'; ?>" id="edituser" class="action" title="<?php echo __('Edit'); ?>"><i class="icon-edit"></i></a>
+    <a href="users.php?id=<?php echo $user->getId(); ?>" title="<?php
+        echo __('Manage User'); ?>" class="action"><i class="icon-share"></i></a>
 </div>
     <table class="custom-info" width="100%">
 <?php foreach ($user->getDynamicData() as $entry) {
@@ -71,8 +72,8 @@ if ($info['error']) {
 <?php if ($org) { ?>
 <div class="tab_content" id="organization-tab" style="display:none">
 <div class="floating-options">
-    <a href="orgs.php?id=<?php echo $org->getId(); ?>" title="Manage Organization"
-        class="action"><i class="icon-share"></i></a>
+    <a href="orgs.php?id=<?php echo $org->getId(); ?>" title="<?php
+    echo __('Manage Organization'); ?>" class="action"><i class="icon-share"></i></a>
 </div>
     <table class="custom-info" width="100%">
 <?php foreach ($org->getDynamicData() as $entry) {
@@ -100,7 +101,8 @@ foreach ($notes as $note)
 <div class="quicknote no-options" id="new-note"
     data-url="users/<?php echo $user->getId(); ?>/note">
 <div class="body">
-    <a href="#"><i class="icon-plus icon-large"></i> &nbsp; Click to create a new note</a>
+    <a href="#"><i class="icon-plus icon-large"></i> &nbsp;
+    <?php echo __('Click to create a new note'); ?></a>
 </div>
 </div>
 </div>
@@ -108,7 +110,9 @@ foreach ($notes as $note)
 
 </div>
 <div id="user-form" style="display:<?php echo $forms ? 'block' : 'none'; ?>;">
-<div><p id="msg_info"><i class="icon-info-sign"></i>&nbsp; Please note that updates will be reflected system-wide.</p></div>
+<div><p id="msg_info"><i class="icon-info-sign"></i>&nbsp; <?php echo __(
+'Please note that updates will be reflected system-wide.'
+); ?></p></div>
 <?php
 $action = $info['action'] ? $info['action'] : ('#users/'.$user->getId());
 if ($ticket && $ticket->getOwnerId() == $user->getId())
@@ -125,13 +129,13 @@ if ($ticket && $ticket->getOwnerId() == $user->getId())
     </table>
     <hr>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="reset" value="Reset">
+        <span class="buttons pull-left">
+            <input type="reset" value="<?php echo __('Reset'); ?>">
             <input type="button" name="cancel" class="<?php
-    echo ($ticket && $user) ? 'cancel' : 'close' ?>"  value="Cancel">
+    echo ($ticket && $user) ? 'cancel' : 'close' ?>"  value="<?php echo __('Cancel'); ?>">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="submit" value="Update User">
+        <span class="buttons pull-right">
+            <input type="submit" value="<?php echo __('Update User'); ?>">
         </span>
      </p>
 </form>
diff --git a/include/staff/templates/users.tmpl.php b/include/staff/templates/users.tmpl.php
index 6bff557b2d4200105b9abab2d54ae68b4d6c90ae..40a2d4952e1c2043917b961b9601cf3a0f0d6c81 100644
--- a/include/staff/templates/users.tmpl.php
+++ b/include/staff/templates/users.tmpl.php
@@ -45,20 +45,22 @@ $from .= ' LEFT JOIN '.TICKET_TABLE.' ticket ON (ticket.user_id = user.id) ';
 $query="$select $from $where GROUP BY user.id ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit();
 //echo $query;
 
-$showing = $search ? 'Search Results: ' : '';
+$showing = $search ? __('Search Results').': ' : '';
 $res = db_query($query);
 if($res && ($num=db_num_rows($res)))
     $showing .= $pageNav->showing();
 else
-    $showing .= "This organization doesn't have any users yet";
+    $showing .= __("This organization doesn't have any users yet");
 
 ?>
-<div style="width:700px; float:left;"><b><?php echo $showing; ?></b></div>
-<div style="float:right;text-align:right;padding-right:5px;">
-    <b><a href="#orgs/<?php echo $org->getId(); ?>/add-user" class="Icon newstaff add-user">Add User</a></b>
+<div style="width:700px;" class="pull-left"><b><?php echo $showing; ?></b></div>
+<div class="pull-right flush-right" style="padding-right:5px;">
+    <b><a href="#orgs/<?php echo $org->getId(); ?>/add-user" class="Icon newstaff add-user"
+        ><?php echo __('Add User'); ?></a></b>
     |
     <b><a href="#orgs/<?php echo $org->getId(); ?>/import-users" class="add-user">
-    <i class="icon-cloud-upload icon-large"></i> Import</a></b>
+    <i class="icon-cloud-upload icon-large"></i>
+    <?php echo __('Import'); ?></a></b>
 </div>
 <div class="clear"></div>
 <br/>
@@ -73,10 +75,10 @@ if ($num) { ?>
     <thead>
         <tr>
             <th width="7px">&nbsp;</th>
-            <th width="350"> Name</th>
-            <th width="300"> Email</th>
-            <th width="100"> Status</th>
-            <th width="100"> Created</th>
+            <th width="350"><?php echo __('Name'); ?></th>
+            <th width="300"><?php echo __('Email'); ?></th>
+            <th width="100"><?php echo __('Status'); ?></th>
+            <th width="100"><?php echo __('Created'); ?></th>
         </tr>
     </thead>
     <tbody>
@@ -121,13 +123,13 @@ if ($num) { ?>
             <?php
             if ($res && $num) {
                 ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select'); ?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All'); ?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None'); ?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle'); ?></a>&nbsp;&nbsp;
             <?php
             } else {
-                echo 'No users found';
+                echo __('No users found!');
             }
             ?>
         </td>
@@ -136,11 +138,11 @@ if ($num) { ?>
 </table>
 <?php
 if ($res && $num) { //Show options..
-    echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;</div>';
+    echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;</div>';
 
     ?>
     <p class="centered" id="actions">
-        <input class="button" type="submit" name="remove-users" value="Remove" >
+        <input class="button" type="submit" name="remove-users" value="<?php echo __('Remove'); ?>" >
     </p>
 <?php
 }
@@ -150,21 +152,23 @@ if ($res && $num) { //Show options..
 } ?>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm'); ?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="remove-users-confirm">
-        Are you sure want to <b>REMOVE</b> selected user from <strong><?php
-        echo $org->getName(); ?></strong> organization?
+        <?php echo sprintf(__(
+        'Are you sure you want to <b>REMOVE</b> %1$s from <strong>%2$s</strong>?'),
+        _N('selected user', 'selected users', 2),
+        $org->getName()); ?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.'); ?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel'); ?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!'); ?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
diff --git a/include/staff/ticket-edit.inc.php b/include/staff/ticket-edit.inc.php
index cf057bda5b522c05ceecd205e0adc44f19a8cff3..2001e28505a525d15f0244f8d809c0e88d33bb8a 100644
--- a/include/staff/ticket-edit.inc.php
+++ b/include/staff/ticket-edit.inc.php
@@ -11,19 +11,19 @@ if ($_POST)
  <input type="hidden" name="do" value="update">
  <input type="hidden" name="a" value="edit">
  <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>">
- <h2>Update Ticket #<?php echo $ticket->getNumber(); ?></h2>
+ <h2><?php echo sprintf(__('Update Ticket #%s'),$ticket->getNumber());?></h2>
  <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
     <tbody>
         <tr>
             <th colspan="2">
-                <em><strong>User Information</strong>: Currently selected user</em>
+                <em><strong><?php echo __('User Information'); ?></strong>: <?php echo __('Currently selected user'); ?></em>
             </th>
         </tr>
     <?php
     if(!$info['user_id'] || !($user = User::lookup($info['user_id'])))
         $user = $ticket->getUser();
     ?>
-    <tr><td>User:</td><td>
+    <tr><td><?php echo __('User'); ?>:</td><td>
         <div id="client-info">
             <a href="#" onclick="javascript:
                 $.userLookup('ajax.php/users/<?php echo $ticket->getOwnerId(); ?>/edit',
@@ -36,7 +36,7 @@ if ($_POST)
             <span id="client-name"><?php echo Format::htmlchars($user->getName()); ?></span>
             &lt;<span id="client-email"><?php echo $user->getEmail(); ?></span>&gt;
             </a>
-            <a class="action-button" style="float:none;overflow:inherit" href="#"
+            <a class="action-button" style="overflow:inherit" href="#"
                 onclick="javascript:
                     $.userLookup('ajax.php/tickets/<?php echo $ticket->getId(); ?>/change-user',
                             function(user) {
@@ -45,7 +45,7 @@ if ($_POST)
                                 $('#client-email').text('<'+user.email+'>');
                     });
                     return false;
-                "><i class="icon-edit"></i> Change</a>
+                "><i class="icon-edit"></i> <?php echo __('Change'); ?></a>
             <input type="hidden" name="user_id" id="user_id"
                 value="<?php echo $info['user_id']; ?>" />
         </div>
@@ -53,32 +53,32 @@ if ($_POST)
     <tbody>
         <tr>
             <th colspan="2">
-                <em><strong>Ticket Information</strong>: Due date overrides SLA's grace period.</em>
+            <em><strong><?php echo __('Ticket Information'); ?></strong>: <?php echo __("Due date overrides SLA's grace period."); ?></em>
             </th>
         </tr>
         <tr>
             <td width="160" class="required">
-                Ticket Source:
+                <?php echo __('Ticket Source');?>:
             </td>
             <td>
                 <select name="source">
-                    <option value="" selected >&mdash; Select Source &mdash;</option>
-                    <option value="Phone" <?php echo ($info['source']=='Phone')?'selected="selected"':''; ?>>Phone</option>
-                    <option value="Email" <?php echo ($info['source']=='Email')?'selected="selected"':''; ?>>Email</option>
-                    <option value="Web"   <?php echo ($info['source']=='Web')?'selected="selected"':''; ?>>Web</option>
-                    <option value="API"   <?php echo ($info['source']=='API')?'selected="selected"':''; ?>>API</option>
-                    <option value="Other" <?php echo ($info['source']=='Other')?'selected="selected"':''; ?>>Other</option>
+                    <option value="" selected >&mdash; <?php echo __('Select Source');?> &mdash;</option>
+                    <option value="Phone" <?php echo ($info['source']=='Phone')?'selected="selected"':''; ?>><?php echo __('Phone');?></option>
+                    <option value="Email" <?php echo ($info['source']=='Email')?'selected="selected"':''; ?>><?php echo __('Email');?></option>
+                    <option value="Web"   <?php echo ($info['source']=='Web')?'selected="selected"':''; ?>><?php echo __('Web');?></option>
+                    <option value="API"   <?php echo ($info['source']=='API')?'selected="selected"':''; ?>><?php echo __('API');?></option>
+                    <option value="Other" <?php echo ($info['source']=='Other')?'selected="selected"':''; ?>><?php echo __('Other');?></option>
                 </select>
                 &nbsp;<font class="error"><b>*</b>&nbsp;<?php echo $errors['source']; ?></font>
             </td>
         </tr>
         <tr>
             <td width="160" class="required">
-                Help Topic:
+                <?php echo __('Help Topic');?>:
             </td>
             <td>
                 <select name="topicId">
-                    <option value="" selected >&mdash; Select Help Topic &mdash;</option>
+                    <option value="" selected >&mdash; <?php echo __('Select Help Topic');?> &mdash;</option>
                     <?php
                     if($topics=Topic::getHelpTopics()) {
                         foreach($topics as $id =>$name) {
@@ -93,11 +93,11 @@ if ($_POST)
         </tr>
         <tr>
             <td width="160">
-                SLA Plan:
+                <?php echo __('SLA Plan');?>:
             </td>
             <td>
                 <select name="slaId">
-                    <option value="0" selected="selected" >&mdash; None &mdash;</option>
+                    <option value="0" selected="selected" >&mdash; <?php echo __('None');?> &mdash;</option>
                     <?php
                     if($slas=SLA::getSLAs()) {
                         foreach($slas as $id =>$name) {
@@ -112,7 +112,7 @@ if ($_POST)
         </tr>
         <tr>
             <td width="160">
-                Due Date:
+                <?php echo __('Due Date');?>:
             </td>
             <td>
                 <input class="dp" id="duedate" name="duedate" value="<?php echo Format::htmlchars($info['duedate']); ?>" size="12" autocomplete=OFF>
@@ -125,7 +125,7 @@ if ($_POST)
                 echo Misc::timeDropdown($hr, $min, 'time');
                 ?>
                 &nbsp;<font class="error">&nbsp;<?php echo $errors['duedate']; ?>&nbsp;<?php echo $errors['time']; ?></font>
-                <em>Time is based on your time zone (GMT <?php echo $thisstaff->getTZoffset(); ?>)</em>
+                <em><?php echo __('Time is based on your time zone');?> (GMT <?php echo $thisstaff->getTZoffset(); ?>)</em>
             </td>
         </tr>
     </tbody>
@@ -134,13 +134,14 @@ if ($_POST)
         <?php if ($forms)
             foreach ($forms as $form) {
                 $form->render(true, false, array('mode'=>'edit','width'=>160,'entry'=>$form));
+                print $form->getForm()->getMedia();
         } ?>
 </table>
 <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
     <tbody>
         <tr>
             <th colspan="2">
-                <em><strong>Internal Note</strong>: Reason for editing the ticket (required) <font class="error">&nbsp;<?php echo $errors['note'];?></font></em>
+                <em><strong><?php echo __('Internal Note');?></strong>: <?php echo __('Reason for editing the ticket (required)');?> <font class="error">&nbsp;<?php echo $errors['note'];?></font></em>
             </th>
         </tr>
         <tr>
@@ -153,9 +154,9 @@ if ($_POST)
     </tbody>
 </table>
 <p style="padding-left:250px;">
-    <input type="submit" name="submit" value="Save">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="tickets.php?id=<?php echo $ticket->getId(); ?>"'>
+    <input type="submit" name="submit" value="<?php echo __('Save');?>">
+    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="tickets.php?id=<?php echo $ticket->getId(); ?>"'>
 </p>
 </form>
 <div style="display:none;" class="dialog draggable" id="user-lookup">
diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php
index 00055c072a2a8e26d67b4b5f231f25896426885a..39acb0864519519c278c7c4d28771476a4042c20 100644
--- a/include/staff/ticket-open.inc.php
+++ b/include/staff/ticket-open.inc.php
@@ -15,12 +15,15 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
     }
 }
 
+if ($_POST)
+    $info['duedate'] = Format::date($cfg->getDateFormat(),
+       strtotime($info['duedate']));
 ?>
 <form action="tickets.php?a=open" method="post" id="save"  enctype="multipart/form-data">
  <?php csrf_token(); ?>
  <input type="hidden" name="do" value="create">
  <input type="hidden" name="a" value="open">
- <h2>Open New Ticket</h2>
+ <h2><?php echo __('Open a New Ticket');?></h2>
  <table class="form_table fixed" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
     <!-- This looks empty - but beware, with fixed table layout, the user
@@ -30,19 +33,19 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
         <tr><td></td><td></td></tr>
         <tr>
             <th colspan="2">
-                <h4>New Ticket</h4>
+                <h4><?php echo __('New Ticket');?></h4>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <th colspan="2">
-                <em><strong>User Information</strong>: </em>
+                <em><strong><?php echo __('User Information'); ?></strong>: </em>
             </th>
         </tr>
         <?php
         if ($user) { ?>
-        <tr><td>User:</td><td>
+        <tr><td><?php echo __('User'); ?>:</td><td>
             <div id="user-info">
                 <input type="hidden" name="uid" id="uid" value="<?php echo $user->getId(); ?>" />
             <a href="#" onclick="javascript:
@@ -56,7 +59,7 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
                 <span id="user-name"><?php echo Format::htmlchars($user->getName()); ?></span>
                 &lt;<span id="user-email"><?php echo $user->getEmail(); ?></span>&gt;
                 </a>
-                <a class="action-button" style="float:none;overflow:inherit" href="#"
+                <a class="action-button" style="overflow:inherit" href="#"
                     onclick="javascript:
                         $.userLookup('ajax.php/users/select/'+$('input#uid').val(),
                             function(user) {
@@ -65,14 +68,14 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
                                 $('#user-email').text('<'+user.email+'>');
                         });
                         return false;
-                "><i class="icon-edit"></i> Change</a>
+                    "><i class="icon-edit"></i> <?php echo __('Change'); ?></a>
             </div>
         </td></tr>
         <?php
         } else { //Fallback: Just ask for email and name
             ?>
         <tr>
-            <td width="160" class="required"> Email Address: </td>
+            <td width="160" class="required"> <?php echo __('Email Address'); ?>: </td>
             <td>
                 <span style="display:inline-block;">
                     <input type="text" size=45 name="email" id="user-email"
@@ -81,7 +84,7 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
             </td>
         </tr>
         <tr>
-            <td width="160" class="required"> Full Name: </td>
+            <td width="160" class="required"> <?php echo __('Full Name'); ?>: </td>
             <td>
                 <span style="display:inline-block;">
                     <input type="text" size=45 name="name" id="user-name" value="<?php echo $info['name']; ?>" /> </span>
@@ -94,9 +97,10 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
         <?php
         if($cfg->notifyONNewStaffTicket()) {  ?>
         <tr>
-            <td width="160">Ticket Notice:</td>
+            <td width="160"><?php echo __('Ticket Notice'); ?>:</td>
             <td>
-            <input type="checkbox" name="alertuser" <?php echo (!$errors || $info['alertuser'])? 'checked="checked"': ''; ?>>Send alert to user.
+            <input type="checkbox" name="alertuser" <?php echo (!$errors || $info['alertuser'])? 'checked="checked"': ''; ?>><?php
+                echo __('Send alert to user.'); ?>
             </td>
         </tr>
         <?php
@@ -105,38 +109,45 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
     <tbody>
         <tr>
             <th colspan="2">
-                <em><strong>Ticket Information &amp; Options</strong>:</em>
+                <em><strong><?php echo __('Ticket Information and Options');?></strong>:</em>
             </th>
         </tr>
         <tr>
             <td width="160" class="required">
-                Ticket Source:
+                <?php echo __('Ticket Source');?>:
             </td>
             <td>
                 <select name="source">
-                    <option value="Phone" <?php echo ($info['source']=='Phone')?'selected="selected"':''; ?> selected="selected">Phone</option>
-                    <option value="Email" <?php echo ($info['source']=='Email')?'selected="selected"':''; ?>>Email</option>
-                    <option value="Other" <?php echo ($info['source']=='Other')?'selected="selected"':''; ?>>Other</option>
+                    <option value="Phone" selected="selected"><?php echo __('Phone'); ?></option>
+                    <option value="Email" <?php echo ($info['source']=='Email')?'selected="selected"':''; ?>><?php echo __('Email'); ?></option>
+                    <option value="Other" <?php echo ($info['source']=='Other')?'selected="selected"':''; ?>><?php echo __('Other'); ?></option>
                 </select>
                 &nbsp;<font class="error"><b>*</b>&nbsp;<?php echo $errors['source']; ?></font>
             </td>
         </tr>
         <tr>
             <td width="160" class="required">
-                Help Topic:
+                <?php echo __('Help Topic'); ?>:
             </td>
             <td>
                 <select name="topicId" onchange="javascript:
                         var data = $(':input[name]', '#dynamic-form').serialize();
-                        $('#dynamic-form').load(
-                            'ajax.php/form/help-topic/' + this.value, data);
-                        ">
+                        $.ajax(
+                          'ajax.php/form/help-topic/' + this.value,
+                          {
+                            data: data,
+                            dataType: 'json',
+                            success: function(json) {
+                              $('#dynamic-form').empty().append(json.html);
+                              $(document.head).append(json.media);
+                            }
+                          });">
                     <?php
                     if ($topics=Topic::getHelpTopics()) {
                         if (count($topics) == 1)
                             $selected = 'selected="selected"';
                         else { ?>
-                <option value="" selected >&mdash; Select Help Topic &mdash;</option>
+                        <option value="" selected >&mdash; <?php echo __('Select Help Topic'); ?> &mdash;</option>
 <?php                   }
                         foreach($topics as $id =>$name) {
                             echo sprintf('<option value="%d" %s %s>%s</option>',
@@ -155,11 +166,11 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
         </tr>
         <tr>
             <td width="160">
-                Department:
+                <?php echo __('Department'); ?>:
             </td>
             <td>
                 <select name="deptId">
-                    <option value="" selected >&mdash; Select Department &mdash;</option>
+                    <option value="" selected >&mdash; <?php echo __('Select Department'); ?>&mdash;</option>
                     <?php
                     if($depts=Dept::getDepartments()) {
                         foreach($depts as $id =>$name) {
@@ -175,11 +186,11 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
 
          <tr>
             <td width="160">
-                SLA Plan:
+                <?php echo __('SLA Plan');?>:
             </td>
             <td>
                 <select name="slaId">
-                    <option value="0" selected="selected" >&mdash; System Default &mdash;</option>
+                    <option value="0" selected="selected" >&mdash; <?php echo __('System Default');?> &mdash;</option>
                     <?php
                     if($slas=SLA::getSLAs()) {
                         foreach($slas as $id =>$name) {
@@ -195,7 +206,7 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
 
          <tr>
             <td width="160">
-                Due Date:
+                <?php echo __('Due Date');?>:
             </td>
             <td>
                 <input class="dp" id="duedate" name="duedate" value="<?php echo Format::htmlchars($info['duedate']); ?>" size="12" autocomplete=OFF>
@@ -208,20 +219,20 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
                 echo Misc::timeDropdown($hr, $min, 'time');
                 ?>
                 &nbsp;<font class="error">&nbsp;<?php echo $errors['duedate']; ?> &nbsp; <?php echo $errors['time']; ?></font>
-                <em>Time is based on your time zone (GMT <?php echo $thisstaff->getTZoffset(); ?>)</em>
+                <em><?php echo __('Time is based on your time zone');?> (GMT <?php echo $thisstaff->getTZoffset(); ?>)</em>
             </td>
         </tr>
 
         <?php
         if($thisstaff->canAssignTickets()) { ?>
         <tr>
-            <td width="160">Assign To:</td>
+            <td width="160"><?php echo __('Assign To');?>:</td>
             <td>
                 <select id="assignId" name="assignId">
-                    <option value="0" selected="selected">&mdash; Select Staff Member OR a Team &mdash;</option>
+                    <option value="0" selected="selected">&mdash; <?php echo __('Select an Agent OR a Team');?> &mdash;</option>
                     <?php
                     if(($users=Staff::getAvailableStaffMembers())) {
-                        echo '<OPTGROUP label="Staff Members ('.count($users).')">';
+                        echo '<OPTGROUP label="'.sprintf(__('Agents (%d)'), count($users)).'">';
                         foreach($users as $id => $name) {
                             $k="s$id";
                             echo sprintf('<option value="%s" %s>%s</option>',
@@ -231,7 +242,7 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
                     }
 
                     if(($teams=Team::getActiveTeams())) {
-                        echo '<OPTGROUP label="Teams ('.count($teams).')">';
+                        echo '<OPTGROUP label="'.sprintf(__('Teams (%d)'), count($teams)).'">';
                         foreach($teams as $id => $name) {
                             $k="t$id";
                             echo sprintf('<option value="%s" %s>%s</option>',
@@ -248,6 +259,7 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
         <tbody id="dynamic-form">
         <?php
             if ($form) {
+                print $form->getForm()->getMedia();
                 include(STAFFINC_DIR .  'templates/dynamic-form.tmpl.php');
             }
         ?>
@@ -264,7 +276,7 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
         if($thisstaff->canPostReply()) { ?>
         <tr>
             <th colspan="2">
-                <em><strong>Response</strong>: Optional response to the above issue.</em>
+                <em><strong><?php echo __('Response');?></strong>: <?php echo __('Optional response to the above issue.');?></em>
             </th>
         </tr>
         <tr>
@@ -273,9 +285,9 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
             if(($cannedResponses=Canned::getCannedResponses())) {
                 ?>
                 <div style="margin-top:0.3em;margin-bottom:0.5em">
-                    Canned Response:&nbsp;
+                    <?php echo __('Canned Response');?>:&nbsp;
                     <select id="cannedResp" name="cannedResp">
-                        <option value="0" selected="selected">&mdash; Select a canned response &mdash;</option>
+                        <option value="0" selected="selected">&mdash; <?php echo __('Select a canned response');?> &mdash;</option>
                         <?php
                         foreach($cannedResponses as $id =>$title) {
                             echo sprintf('<option value="%d">%s</option>',$id,$title);
@@ -283,7 +295,7 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
                         ?>
                     </select>
                     &nbsp;&nbsp;&nbsp;
-                    <label><input type='checkbox' value='1' name="append" id="append" checked="checked">Append</label>
+                    <label><input type='checkbox' value='1' name="append" id="append" checked="checked"><?php echo __('Append');?></label>
                 </div>
             <?php
             }
@@ -295,63 +307,54 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
                     data-signature="<?php
                         echo Format::htmlchars(Format::viewableImages($signature)); ?>"
                     data-signature-field="signature" data-dept-field="deptId"
-                    placeholder="Intial response for the ticket"
+                    placeholder="<?php echo __('Initial response for the ticket'); ?>"
                     name="response" id="response" cols="21" rows="8"
                     style="width:80%;"><?php echo $info['response']; ?></textarea>
-                <table border="0" cellspacing="0" cellpadding="2" width="100%">
-                <?php
-                if($cfg->allowAttachments()) { ?>
-                    <tr><td width="100" valign="top">Attachments:</td>
-                        <td>
-                            <div class="canned_attachments">
-                            <?php
-                            if($info['cannedattachments']) {
-                                foreach($info['cannedattachments'] as $k=>$id) {
-                                    if(!($file=AttachmentFile::lookup($id))) continue;
-                                    $hash=$file->getKey().md5($file->getId().session_id().$file->getKey());
-                                    echo sprintf('<label><input type="checkbox" name="cannedattachments[]"
-                                            id="f%d" value="%d" checked="checked"
-                                            <a href="file.php?h=%s">%s</a>&nbsp;&nbsp;</label>&nbsp;',
-                                            $file->getId(), $file->getId() , $hash, $file->getName());
-                                }
-                            }
-                            ?>
-                            </div>
-                            <div class="uploads"></div>
-                            <div class="file_input">
-                                <input type="file" class="multifile" name="attachments[]" size="30" value="" />
-                            </div>
-                        </td>
-                    </tr>
-                <?php
-                } ?>
+                    <div class="attachments">
+<?php
+print $response_form->getField('attachments')->render();
+?>
+                    </div>
 
-            <?php
-            if($thisstaff->canCloseTickets()) { ?>
-                <tr>
-                    <td width="100">Ticket Status:</td>
-                    <td>
-                        <input type="checkbox" name="ticket_state" value="closed" <?php echo $info['ticket_state']?'checked="checked"':''; ?>>
-                        <b>Close On Response</b>&nbsp;<em>(Only applicable if response is entered)</em>
-                    </td>
-                </tr>
-            <?php
-            } ?>
+                <table border="0" cellspacing="0" cellpadding="2" width="100%">
+            <tr>
+                <td width="100"><?php echo __('Ticket Status');?>:</td>
+                <td>
+                    <select name="statusId">
+                    <?php
+                    $statusId = $info['statusId'] ?: $cfg->getDefaultTicketStatusId();
+                    $states = array('open');
+                    if ($thisstaff->canCloseTickets())
+                        $states = array_merge($states, array('closed'));
+                    foreach (TicketStatusList::getStatuses(
+                                array('states' => $states)) as $s) {
+                        if (!$s->isEnabled()) continue;
+                        $selected = ($statusId == $s->getId());
+                        echo sprintf('<option value="%d" %s>%s</option>',
+                                $s->getId(),
+                                $selected
+                                 ? 'selected="selected"' : '',
+                                __($s->getName()));
+                    }
+                    ?>
+                    </select>
+                </td>
+            </tr>
              <tr>
-                <td width="100">Signature:</td>
+                <td width="100"><?php echo __('Signature');?>:</td>
                 <td>
                     <?php
                     $info['signature']=$info['signature']?$info['signature']:$thisstaff->getDefaultSignatureType();
                     ?>
-                    <label><input type="radio" name="signature" value="none" checked="checked"> None</label>
+                    <label><input type="radio" name="signature" value="none" checked="checked"> <?php echo __('None');?></label>
                     <?php
                     if($thisstaff->getSignature()) { ?>
                         <label><input type="radio" name="signature" value="mine"
-                            <?php echo ($info['signature']=='mine')?'checked="checked"':''; ?>> My signature</label>
+                            <?php echo ($info['signature']=='mine')?'checked="checked"':''; ?>> <?php echo __('My signature');?></label>
                     <?php
                     } ?>
                     <label><input type="radio" name="signature" value="dept"
-                        <?php echo ($info['signature']=='dept')?'checked="checked"':''; ?>> Dept. Signature (if set)</label>
+                        <?php echo ($info['signature']=='dept')?'checked="checked"':''; ?>> <?php echo sprintf(__('Department Signature (%s)'), __('if set')); ?></label>
                 </td>
              </tr>
             </table>
@@ -362,14 +365,14 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
         ?>
         <tr>
             <th colspan="2">
-                <em><strong>Internal Note</strong>
+                <em><strong><?php echo __('Internal Note');?></strong>
                 <font class="error">&nbsp;<?php echo $errors['note']; ?></font></em>
             </th>
         </tr>
         <tr>
             <td colspan=2>
                 <textarea class="richtext ifhtml draft draft-delete"
-                    placeholder="Optional internal note (recommended on assignment)"
+                    placeholder="<?php echo __('Optional internal note (recommended on assignment)'); ?>"
                     data-draft-namespace="ticket.staff.note" name="note"
                     cols="21" rows="6" style="width:80%;"
                     ><?php echo $info['note']; ?></textarea>
@@ -378,9 +381,9 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) {
     </tbody>
 </table>
 <p style="text-align:center;">
-    <input type="submit" name="submit" value="Open">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick="javascript:
+    <input type="submit" name="submit" value="<?php echo _P('action-button', 'Open');?>">
+    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick="javascript:
         $('.richtext').each(function() {
             var redactor = $(this).data('redactor');
             if (redactor && redactor.opts.draftDelete)
@@ -414,9 +417,11 @@ $(function() {
     // Popup user lookup on the initial page load (not post) if we don't have a
     // user selected
     if (!$_POST && !$user) {?>
-    $.userLookup('ajax.php/users/lookup/form', function (user) {
+    setTimeout(function() {
+      $.userLookup('ajax.php/users/lookup/form', function (user) {
         window.location.href = window.location.href+'&uid='+user.id;
-     });
+      });
+    }, 100);
     <?php
     } ?>
 });
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index 665f3a4bfa7f6e9ab781a288dd450fbff2a65e66..078af3a0a268bf45bed22653fcbecfdaef703ba2 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -10,7 +10,7 @@ $info=($_POST && $errors)?Format::input($_POST):array();
 
 //Auto-lock the ticket if locking is enabled.. If already locked by the user then it simply renews.
 if($cfg->getLockTime() && !$ticket->acquireLock($thisstaff->getId(),$cfg->getLockTime()))
-    $warn.='Unable to obtain a lock on the ticket';
+    $warn.=__('Unable to obtain a lock on the ticket');
 
 //Get the goodies.
 $dept  = $ticket->getDept();  //Dept
@@ -22,100 +22,117 @@ $lock  = $ticket->getLock();  //Ticket lock obj
 $id    = $ticket->getId();    //Ticket ID.
 
 //Useful warnings and errors the user might want to know!
-if($ticket->isAssigned() && (
-            ($staff && $staff->getId()!=$thisstaff->getId())
-         || ($team && !$team->hasMember($thisstaff))
+if ($ticket->isClosed() && !$ticket->isReopenable())
+    $warn = sprintf(
+            __('Current ticket status (%s) does not allow the end user to reply.'),
+            $ticket->getStatus());
+elseif ($ticket->isAssigned()
+        && (($staff && $staff->getId()!=$thisstaff->getId())
+            || ($team && !$team->hasMember($thisstaff))
         ))
-    $warn.='&nbsp;&nbsp;<span class="Icon assignedTicket">Ticket is assigned to '.implode('/', $ticket->getAssignees()).'</span>';
-if(!$errors['err'] && ($lock && $lock->getStaffId()!=$thisstaff->getId()))
-    $errors['err']='This ticket is currently locked by '.$lock->getStaffName();
-if(!$errors['err'] && ($emailBanned=TicketFilter::isBanned($ticket->getEmail())))
-    $errors['err']='Email is in banlist! Must be removed before any reply/response';
+    $warn.= sprintf('&nbsp;&nbsp;<span class="Icon assignedTicket">%s</span>',
+            sprintf(__('Ticket is assigned to %s'),
+                implode('/', $ticket->getAssignees())
+                ));
+
+if (!$errors['err']) {
+
+    if ($lock && $lock->getStaffId()!=$thisstaff->getId())
+        $errors['err'] = sprintf(__('This ticket is currently locked by %s'),
+                $lock->getStaffName());
+    elseif (($emailBanned=TicketFilter::isBanned($ticket->getEmail())))
+        $errors['err'] = __('Email is in banlist! Must be removed before any reply/response');
+}
 
 $unbannable=($emailBanned) ? BanList::includes($ticket->getEmail()) : false;
 
 if($ticket->isOverdue())
-    $warn.='&nbsp;&nbsp;<span class="Icon overdueTicket">Marked overdue!</span>';
+    $warn.='&nbsp;&nbsp;<span class="Icon overdueTicket">'.__('Marked overdue!').'</span>';
 
 ?>
 <table width="940" cellpadding="2" cellspacing="0" border="0">
     <tr>
-        <td width="50%" class="has_bottom_border">
+        <td width="20%" class="has_bottom_border">
              <h2><a href="tickets.php?id=<?php echo $ticket->getId(); ?>"
-             title="Reload"><i class="icon-refresh"></i> Ticket #<?php echo $ticket->getNumber(); ?></a></h2>
+             title="<?php echo __('Reload'); ?>"><i class="icon-refresh"></i>
+             <?php echo sprintf(__('Ticket #%s'), $ticket->getNumber()); ?></a></h2>
         </td>
-        <td width="50%" class="right_align has_bottom_border">
+        <td width="auto" class="flush-right has_bottom_border">
             <?php
             if ($thisstaff->canBanEmails()
                     || $thisstaff->canEditTickets()
                     || ($dept && $dept->isManager($thisstaff))) { ?>
-            <span class="action-button" data-dropdown="#action-dropdown-more">
-                <span ><i class="icon-cog"></i> More</span>
-                <i class="icon-caret-down"></i>
+            <span class="action-button pull-right" data-dropdown="#action-dropdown-more">
+                <i class="icon-caret-down pull-right"></i>
+                <span ><i class="icon-cog"></i> <?php echo __('More');?></span>
             </span>
             <?php
-            } ?>
-            <?php if($thisstaff->canDeleteTickets()) { ?>
-                <a id="ticket-delete" class="action-button confirm-action" href="#delete"><i class="icon-trash"></i> Delete</a>
-            <?php } ?>
-            <?php
-            if($thisstaff->canCloseTickets()) {
-                if($ticket->isOpen()) {?>
-                <a id="ticket-close" class="action-button" href="#close"><i class="icon-remove-circle"></i> Close</a>
-                <?php
-                } else { ?>
-                <a id="ticket-reopen" class="action-button" href="#reopen"><i class="icon-undo"></i> Reopen</a>
-                <?php
-                } ?>
-            <?php
-            } ?>
-            <?php
-            if($thisstaff->canEditTickets()) { ?>
-                <a class="action-button" href="tickets.php?id=<?php echo $ticket->getId(); ?>&a=edit"><i class="icon-edit"></i> Edit</a>
-            <?php
-            } ?>
+            }
+            // Status change options
+            echo TicketStatus::status_options();
+
+            if ($thisstaff->canEditTickets()) { ?>
+                <a class="action-button pull-right" href="tickets.php?id=<?php echo $ticket->getId(); ?>&a=edit"><i class="icon-edit"></i> <?php
+                    echo __('Edit'); ?></a>
             <?php
-            if($ticket->isOpen() && !$ticket->isAssigned() && $thisstaff->canAssignTickets()) {?>
-                <a id="ticket-claim" class="action-button confirm-action" href="#claim"><i class="icon-user"></i> Claim</a>
+            }
+            if ($ticket->isOpen() && !$ticket->isAssigned() && $thisstaff->canAssignTickets()) {?>
+                <a id="ticket-claim" class="action-button pull-right confirm-action" href="#claim"><i class="icon-user"></i> <?php
+                    echo __('Claim'); ?></a>
 
             <?php
             }?>
-            <span class="action-button" data-dropdown="#action-dropdown-print">
-                <a id="ticket-print" href="tickets.php?id=<?php echo $ticket->getId(); ?>&a=print"><i class="icon-print"></i> Print</a>
-                <i class="icon-caret-down"></i>
+            <span class="action-button pull-right" data-dropdown="#action-dropdown-print">
+                <i class="icon-caret-down pull-right"></i>
+                <a id="ticket-print" href="tickets.php?id=<?php echo $ticket->getId(); ?>&a=print"><i class="icon-print"></i> <?php
+                    echo __('Print'); ?></a>
             </span>
             <div id="action-dropdown-print" class="action-dropdown anchor-right">
               <ul>
                  <li><a class="no-pjax" target="_blank" href="tickets.php?id=<?php echo $ticket->getId(); ?>&a=print&notes=0"><i
-                 class="icon-file-alt"></i> Ticket Thread</a>
+                 class="icon-file-alt"></i> <?php echo __('Ticket Thread'); ?></a>
                  <li><a class="no-pjax" target="_blank" href="tickets.php?id=<?php echo $ticket->getId(); ?>&a=print&notes=1"><i
-                 class="icon-file-text-alt"></i> Thread + Internal Notes</a>
+                 class="icon-file-text-alt"></i> <?php echo __('Thread + Internal Notes'); ?></a>
               </ul>
             </div>
             <div id="action-dropdown-more" class="action-dropdown anchor-right">
               <ul>
                 <?php
                  if($thisstaff->canEditTickets()) { ?>
-                    <li><a class="change-user" href="#tickets/<?php echo $ticket->getId(); ?>/change-user"><i class="icon-user"></i> Change Ticket Owner</a></li>
+                    <li><a class="change-user" href="#tickets/<?php
+                    echo $ticket->getId(); ?>/change-user"><i class="icon-user"></i> <?php
+                    echo __('Change Owner'); ?></a></li>
+                <?php
+                 }
+                 if($thisstaff->canDeleteTickets()) {
+                     ?>
+                    <li><a class="ticket-action" href="#tickets/<?php
+                    echo $ticket->getId(); ?>/status/delete"
+                    data-href="tickets.php"><i class="icon-trash"></i> <?php
+                    echo __('Delete Ticket'); ?></a></li>
                 <?php
                  }
                 if($ticket->isOpen() && ($dept && $dept->isManager($thisstaff))) {
 
                     if($ticket->isAssigned()) { ?>
-                        <li><a  class="confirm-action" id="ticket-release" href="#release"><i class="icon-user"></i> Release (unassign) Ticket</a></li>
+                        <li><a  class="confirm-action" id="ticket-release" href="#release"><i class="icon-user"></i> <?php
+                            echo __('Release (unassign) Ticket'); ?></a></li>
                     <?php
                     }
 
                     if(!$ticket->isOverdue()) { ?>
-                        <li><a class="confirm-action" id="ticket-overdue" href="#overdue"><i class="icon-bell"></i> Mark as Overdue</a></li>
+                        <li><a class="confirm-action" id="ticket-overdue" href="#overdue"><i class="icon-bell"></i> <?php
+                            echo __('Mark as Overdue'); ?></a></li>
                     <?php
                     }
 
                     if($ticket->isAnswered()) { ?>
-                        <li><a class="confirm-action" id="ticket-unanswered" href="#unanswered"><i class="icon-circle-arrow-left"></i> Mark as Unanswered</a></li>
+                    <li><a class="confirm-action" id="ticket-unanswered" href="#unanswered"><i class="icon-circle-arrow-left"></i> <?php
+                            echo __('Mark as Unanswered'); ?></a></li>
                     <?php
                     } else { ?>
-                        <li><a class="confirm-action" id="ticket-answered" href="#answered"><i class="icon-circle-arrow-right"></i> Mark as Answered</a></li>
+                    <li><a class="confirm-action" id="ticket-answered" href="#answered"><i class="icon-circle-arrow-right"></i> <?php
+                            echo __('Mark as Answered'); ?></a></li>
                     <?php
                     }
                 } ?>
@@ -123,16 +140,20 @@ if($ticket->isOverdue())
                     ?>/forms/manage" onclick="javascript:
                     $.dialog($(this).attr('href').substr(1), 201);
                     return false"
-                    ><i class="icon-paste"></i> Manage Forms</a></li>
+                    ><i class="icon-paste"></i> <?php echo __('Manage Forms'); ?></a></li>
 
 <?php           if($thisstaff->canBanEmails()) {
                      if(!$emailBanned) {?>
                         <li><a class="confirm-action" id="ticket-banemail"
-                            href="#banemail"><i class="icon-ban-circle"></i> Ban Email (<?php echo $ticket->getEmail(); ?>)</a></li>
+                            href="#banemail"><i class="icon-ban-circle"></i> <?php echo sprintf(
+                                Format::htmlchars(__('Ban Email <%s>')),
+                                $ticket->getEmail()); ?></a></li>
                 <?php
                      } elseif($unbannable) { ?>
                         <li><a  class="confirm-action" id="ticket-banemail"
-                            href="#unbanemail"><i class="icon-undo"></i> Unban Email (<?php echo $ticket->getEmail(); ?>)</a></li>
+                            href="#unbanemail"><i class="icon-undo"></i> <?php echo sprintf(
+                                Format::htmlchars(__('Unban Email <%s>')),
+                                $ticket->getEmail()); ?></a></li>
                     <?php
                      }
                 }?>
@@ -146,19 +167,19 @@ if($ticket->isOverdue())
         <td width="50%">
             <table border="0" cellspacing="" cellpadding="4" width="100%">
                 <tr>
-                    <th width="100">Status:</th>
-                    <td><?php echo ucfirst($ticket->getStatus()); ?></td>
+                    <th width="100"><?php echo __('Status');?>:</th>
+                    <td><?php echo $ticket->getStatus(); ?></td>
                 </tr>
                 <tr>
-                    <th>Priority:</th>
+                    <th><?php echo __('Priority');?>:</th>
                     <td><?php echo $ticket->getPriority(); ?></td>
                 </tr>
                 <tr>
-                    <th>Department:</th>
+                    <th><?php echo __('Department');?>:</th>
                     <td><?php echo Format::htmlchars($ticket->getDeptName()); ?></td>
                 </tr>
                 <tr>
-                    <th>Create Date:</th>
+                    <th><?php echo __('Create Date');?>:</th>
                     <td><?php echo Format::db_datetime($ticket->getCreateDate()); ?></td>
                 </tr>
             </table>
@@ -166,7 +187,7 @@ if($ticket->isOverdue())
         <td width="50%" style="vertical-align:top">
             <table border="0" cellspacing="" cellpadding="4" width="100%">
                 <tr>
-                    <th width="100">User:</th>
+                    <th width="100"><?php echo __('User'); ?>:</th>
                     <td><a href="#tickets/<?php echo $ticket->getId(); ?>/user"
                         onclick="javascript:
                             $.userLookup('ajax.php/tickets/<?php echo $ticket->getId(); ?>/user',
@@ -182,25 +203,29 @@ if($ticket->isOverdue())
                         ?></span></a>
                         <?php
                         if($user) {
-                            echo sprintf('&nbsp;&nbsp;<a href="tickets.php?a=search&uid=%d" title="Related Tickets" data-dropdown="#action-dropdown-stats">(<b>%d</b>)</a>',
-                                    urlencode($user->getId()), $user->getNumTickets());
+                            echo sprintf('&nbsp;&nbsp;<a href="tickets.php?a=search&uid=%d" title="%s" data-dropdown="#action-dropdown-stats">(<b>%d</b>)</a>',
+                                    urlencode($user->getId()), __('Related Tickets'), $user->getNumTickets());
                         ?>
                             <div id="action-dropdown-stats" class="action-dropdown anchor-right">
                                 <ul>
                                     <?php
                                     if(($open=$user->getNumOpenTickets()))
-                                        echo sprintf('<li><a href="tickets.php?a=search&status=open&uid=%s"><i class="icon-folder-open-alt icon-fixed-width"></i> %d Open Tickets</a></li>',
-                                                $user->getId(), $open);
+                                        echo sprintf('<li><a href="tickets.php?a=search&status=open&uid=%s"><i class="icon-folder-open-alt icon-fixed-width"></i> %s</a></li>',
+                                                $user->getId(), sprintf(_N('%d Open Ticket', '%d Open Tickets', $open), $open));
+
                                     if(($closed=$user->getNumClosedTickets()))
-                                        echo sprintf('<li><a href="tickets.php?a=search&status=closed&uid=%d"><i class="icon-folder-close-alt icon-fixed-width"></i> %d Closed Tickets</a></li>',
-                                                $user->getId(), $closed);
+                                        echo sprintf('<li><a href="tickets.php?a=search&status=closed&uid=%d"><i
+                                                class="icon-folder-close-alt icon-fixed-width"></i> %s</a></li>',
+                                                $user->getId(), sprintf(_N('%d Closed Ticket', '%d Closed Tickets', $closed), $closed));
                                     ?>
-                                    <li><a href="tickets.php?a=search&uid=<?php echo $ticket->getOwnerId(); ?>"><i class="icon-double-angle-right icon-fixed-width"></i> All Tickets</a></li>
+                                    <li><a href="tickets.php?a=search&uid=<?php echo $ticket->getOwnerId(); ?>"><i class="icon-double-angle-right icon-fixed-width"></i> <?php echo __('All Tickets'); ?></a></li>
                                     <li><a href="users.php?id=<?php echo
                                     $user->getId(); ?>"><i class="icon-user
-                                    icon-fixed-width"></i> Manage User</a></li>
+                                    icon-fixed-width"></i> <?php echo __('Manage User'); ?></a></li>
 <?php if ($user->getOrgId()) { ?>
-                                    <li><a href="orgs.php?id=<?php echo $user->getOrgId(); ?>"><i class="icon-building icon-fixed-width"></i> Manage Organization</a></li>
+                                    <li><a href="orgs.php?id=<?php echo $user->getOrgId(); ?>"><i
+                                        class="icon-building icon-fixed-width"></i> <?php
+                                        echo __('Manage Organization'); ?></a></li>
 <?php } ?>
                                 </ul>
                             </div>
@@ -210,19 +235,19 @@ if($ticket->isOverdue())
                     </td>
                 </tr>
                 <tr>
-                    <th>Email:</th>
+                    <th><?php echo __('Email'); ?>:</th>
                     <td>
                         <span id="user-<?php echo $ticket->getOwnerId(); ?>-email"><?php echo $ticket->getEmail(); ?></span>
                     </td>
                 </tr>
                 <tr>
-                    <th>Phone:</th>
+                    <th><?php echo __('Phone'); ?>:</th>
                     <td>
                         <span id="user-<?php echo $ticket->getOwnerId(); ?>-phone"><?php echo $ticket->getPhoneNumber(); ?></span>
                     </td>
                 </tr>
                 <tr>
-                    <th>Source:</th>
+                    <th><?php echo __('Source'); ?>:</th>
                     <td><?php
                         echo Format::htmlchars($ticket->getSource());
 
@@ -243,45 +268,45 @@ if($ticket->isOverdue())
                 <?php
                 if($ticket->isOpen()) { ?>
                 <tr>
-                    <th width="100">Assigned To:</th>
+                    <th width="100"><?php echo __('Assigned To');?>:</th>
                     <td>
                         <?php
                         if($ticket->isAssigned())
                             echo Format::htmlchars(implode('/', $ticket->getAssignees()));
                         else
-                            echo '<span class="faded">&mdash; Unassigned &mdash;</span>';
+                            echo '<span class="faded">&mdash; '.__('Unassigned').' &mdash;</span>';
                         ?>
                     </td>
                 </tr>
                 <?php
                 } else { ?>
                 <tr>
-                    <th width="100">Closed By:</th>
+                    <th width="100"><?php echo __('Closed By');?>:</th>
                     <td>
                         <?php
                         if(($staff = $ticket->getStaff()))
                             echo Format::htmlchars($staff->getName());
                         else
-                            echo '<span class="faded">&mdash; Unknown &mdash;</span>';
+                            echo '<span class="faded">&mdash; '.__('Unknown').' &mdash;</span>';
                         ?>
                     </td>
                 </tr>
                 <?php
                 } ?>
                 <tr>
-                    <th>SLA Plan:</th>
-                    <td><?php echo $sla?Format::htmlchars($sla->getName()):'<span class="faded">&mdash; none &mdash;</span>'; ?></td>
+                    <th><?php echo __('SLA Plan');?>:</th>
+                    <td><?php echo $sla?Format::htmlchars($sla->getName()):'<span class="faded">&mdash; '.__('None').' &mdash;</span>'; ?></td>
                 </tr>
                 <?php
                 if($ticket->isOpen()){ ?>
                 <tr>
-                    <th>Due Date:</th>
+                    <th><?php echo __('Due Date');?>:</th>
                     <td><?php echo Format::db_datetime($ticket->getEstDueDate()); ?></td>
                 </tr>
                 <?php
                 }else { ?>
                 <tr>
-                    <th>Close Date:</th>
+                    <th><?php echo __('Close Date');?>:</th>
                     <td><?php echo Format::db_datetime($ticket->getCloseDate()); ?></td>
                 </tr>
                 <?php
@@ -292,15 +317,15 @@ if($ticket->isOverdue())
         <td width="50%">
             <table cellspacing="0" cellpadding="4" width="100%" border="0">
                 <tr>
-                    <th width="100">Help Topic:</th>
+                    <th width="100"><?php echo __('Help Topic');?>:</th>
                     <td><?php echo Format::htmlchars($ticket->getHelpTopic()); ?></td>
                 </tr>
                 <tr>
-                    <th nowrap>Last Message:</th>
+                    <th nowrap><?php echo __('Last Message');?>:</th>
                     <td><?php echo Format::db_datetime($ticket->getLastMsgDate()); ?></td>
                 </tr>
                 <tr>
-                    <th nowrap>Last Response:</th>
+                    <th nowrap><?php echo __('Last Response');?>:</th>
                     <td><?php echo Format::db_datetime($ticket->getLastRespDate()); ?></td>
                 </tr>
             </table>
@@ -351,7 +376,7 @@ $tcount = $ticket->getThreadCount();
 $tcount+= $ticket->getNumNotes();
 ?>
 <ul id="threads">
-    <li><a class="active" id="toggle_ticket_thread" href="#">Ticket Thread (<?php echo $tcount; ?>)</a></li>
+    <li><a class="active" id="toggle_ticket_thread" href="#"><?php echo sprintf(__('Ticket Thread (%d)'), $tcount); ?></a></li>
 </ul>
 <div id="ticket_thread">
     <?php
@@ -364,11 +389,13 @@ $tcount+= $ticket->getNumNotes();
             <tr>
                 <th colspan="4" width="100%">
                 <div>
+                    <span class="pull-left">
                     <span style="display:inline-block"><?php
                         echo Format::db_datetime($entry['created']);?></span>
-                    <span style="display:inline-block;padding-left:1em" class="faded title"><?php
+                    <span style="display:inline-block;padding:0 1em" class="faded title"><?php
                         echo Format::truncate($entry['title'], 100); ?></span>
-                    <span style="float:right;white-space:no-wrap;display:inline-block">
+                    </span>
+                    <span class="pull-right" style="white-space:no-wrap;display:inline-block">
                         <span style="vertical-align:middle;" class="textra"></span>
                         <span style="vertical-align:middle;"
                             class="tmeta faded title"><?php
@@ -404,7 +431,7 @@ $tcount+= $ticket->getNumNotes();
             $msgId=$entry['id'];
        }
     } else {
-        echo '<p>Error fetching ticket thread - get technical help.</p>';
+        echo '<p>'.__('Error fetching ticket thread - get technical help.').'</p>';
     }?>
 </div>
 <div class="clear" style="padding-bottom:10px;"></div>
@@ -420,18 +447,18 @@ $tcount+= $ticket->getNumNotes();
     <ul class="tabs">
         <?php
         if($thisstaff->canPostReply()) { ?>
-        <li><a id="reply_tab" href="#reply">Post Reply</a></li>
+        <li><a id="reply_tab" href="#reply"><?php echo __('Post Reply');?></a></li>
         <?php
         } ?>
-        <li><a id="note_tab" href="#note">Post Internal Note</a></li>
+        <li><a id="note_tab" href="#note"><?php echo __('Post Internal Note');?></a></li>
         <?php
         if($thisstaff->canTransferTickets()) { ?>
-        <li><a id="transfer_tab" href="#transfer">Dept. Transfer</a></li>
+        <li><a id="transfer_tab" href="#transfer"><?php echo __('Department Transfer');?></a></li>
         <?php
         }
 
         if($thisstaff->canAssignTickets()) { ?>
-        <li><a id="assign_tab" href="#assign"><?php echo $ticket->isAssigned()?'Reassign Ticket':'Assign Ticket'; ?></a></li>
+        <li><a id="assign_tab" href="#assign"><?php echo $ticket->isAssigned()?__('Reassign Ticket'):__('Assign Ticket'); ?></a></li>
         <?php
         } ?>
     </ul>
@@ -447,7 +474,7 @@ $tcount+= $ticket->getNumNotes();
            <tbody id="to_sec">
             <tr>
                 <td width="120">
-                    <label><strong>TO:</strong></label>
+                    <label><strong><?php echo __('To'); ?>:</strong></label>
                 </td>
                 <td>
                     <?php
@@ -460,7 +487,7 @@ $tcount+= $ticket->getNumNotes();
                     <select id="emailreply" name="emailreply">
                         <option value="1" <?php echo $emailReply ?  'selected="selected"' : ''; ?>><?php echo $to; ?></option>
                         <option value="0" <?php echo !$emailReply ? 'selected="selected"' : ''; ?>
-                            >&mdash;Do Not Email Reply&mdash;</option>
+                        >&mdash; <?php echo __('Do Not Email Reply'); ?> &mdash;</option>
                     </select>
                 </td>
             </tr>
@@ -472,7 +499,7 @@ $tcount+= $ticket->getNumNotes();
                 style="display:<?php echo $emailReply?  'table-row-group':'none'; ?>;">
              <tr>
                 <td width="120">
-                    <label><strong>Collaborators:</strong></label>
+                    <label><strong><?php echo __('Collaborators'); ?>:</strong></label>
                 </td>
                 <td>
                     <input type='checkbox' value='1' name="emailcollab" id="emailcollab"
@@ -480,9 +507,9 @@ $tcount+= $ticket->getNumNotes();
                         style="display:<?php echo $ticket->getNumCollaborators() ? 'inline-block': 'none'; ?>;"
                         >
                     <?php
-                    $recipients = 'Add Recipients';
+                    $recipients = __('Add Recipients');
                     if ($ticket->getNumCollaborators())
-                        $recipients = sprintf('Recipients (%d of %d)',
+                        $recipients = sprintf(__('Recipients (%d of %d)'),
                                 $ticket->getNumActiveCollaborators(),
                                 $ticket->getNumCollaborators());
 
@@ -504,17 +531,17 @@ $tcount+= $ticket->getNumNotes();
             }?>
             <tr>
                 <td width="120" style="vertical-align:top">
-                    <label><strong>Response:</strong></label>
+                    <label><strong><?php echo __('Response');?>:</strong></label>
                 </td>
                 <td>
                     <select id="cannedResp" name="cannedResp">
-                        <option value="0" selected="selected">Select a canned response</option>
-                        <option value='original'>Original Message</option>
-                        <option value='lastmessage'>Last Message</option>
+                        <option value="0" selected="selected"><?php echo __('Select a canned response');?></option>
+                        <option value='original'><?php echo __('Original Message'); ?></option>
+                        <option value='lastmessage'><?php echo __('Last Message'); ?></option>
                         <?php
                         if(($cannedResponses=Canned::responsesByDeptId($ticket->getDeptId()))) {
                             echo '<option value="0" disabled="disabled">
-                                ------------- Premade Replies ------------- </option>';
+                                ------------- '.__('Premade Replies').' ------------- </option>';
                             foreach($cannedResponses as $id =>$title)
                                 echo sprintf('<option value="%d">%s</option>',$id,$title);
                         }
@@ -538,82 +565,78 @@ $tcount+= $ticket->getNumNotes();
                         data-signature-field="signature" data-dept-id="<?php echo $dept->getId(); ?>"
                         data-signature="<?php
                             echo Format::htmlchars(Format::viewableImages($signature)); ?>"
-                        placeholder="Start writing your response here. Use canned responses from the drop-down above"
+                        placeholder="<?php echo __(
+                        'Start writing your response here. Use canned responses from the drop-down above'
+                        ); ?>"
                         data-draft-object-id="<?php echo $ticket->getId(); ?>"
                         rows="9" wrap="soft"
                         class="richtext ifhtml draft draft-delete"><?php
                         echo $info['response']; ?></textarea>
+                <div id="reply_form_attachments" class="attachments">
+<?php
+print $response_form->getField('attachments')->render();
+?>
+                </div>
                 </td>
             </tr>
-            <?php
-            if($cfg->allowAttachments()) { ?>
-            <tr>
-                <td width="120" style="vertical-align:top">
-                    <label for="attachment">Attachments:</label>
-                </td>
-                <td id="reply_form_attachments" class="attachments">
-                    <div class="canned_attachments">
-                    </div>
-                    <div class="uploads">
-                    </div>
-                    <div class="file_input">
-                        <input type="file" class="multifile" name="attachments[]" size="30" value="" />
-                    </div>
-                </td>
-            </tr>
-            <?php
-            }?>
             <tr>
                 <td width="120">
-                    <label for="signature" class="left">Signature:</label>
+                    <label for="signature" class="left"><?php echo __('Signature');?>:</label>
                 </td>
                 <td>
                     <?php
                     $info['signature']=$info['signature']?$info['signature']:$thisstaff->getDefaultSignatureType();
                     ?>
-                    <label><input type="radio" name="signature" value="none" checked="checked"> None</label>
+                    <label><input type="radio" name="signature" value="none" checked="checked"> <?php echo __('None');?></label>
                     <?php
                     if($thisstaff->getSignature()) {?>
                     <label><input type="radio" name="signature" value="mine"
-                        <?php echo ($info['signature']=='mine')?'checked="checked"':''; ?>> My signature</label>
+                        <?php echo ($info['signature']=='mine')?'checked="checked"':''; ?>> <?php echo __('My Signature');?></label>
                     <?php
                     } ?>
                     <?php
                     if($dept && $dept->canAppendSignature()) { ?>
                     <label><input type="radio" name="signature" value="dept"
                         <?php echo ($info['signature']=='dept')?'checked="checked"':''; ?>>
-                        Dept. Signature (<?php echo Format::htmlchars($dept->getName()); ?>)</label>
+                        <?php echo sprintf(__('Department Signature (%s)'), Format::htmlchars($dept->getName())); ?></label>
                     <?php
                     } ?>
                 </td>
             </tr>
-            <?php
-            if($ticket->isClosed() || $thisstaff->canCloseTickets()) { ?>
             <tr>
                 <td width="120">
-                    <label><strong>Ticket Status:</strong></label>
+                    <label><strong><?php echo __('Ticket Status');?>:</strong></label>
                 </td>
                 <td>
+                    <select name="reply_status_id">
                     <?php
-                    $statusChecked=isset($info['reply_ticket_status'])?'checked="checked"':'';
-                    if($ticket->isClosed()) { ?>
-                        <label><input type="checkbox" name="reply_ticket_status" id="reply_ticket_status" value="Open"
-                            <?php echo $statusChecked; ?>> Reopen on Reply</label>
-                   <?php
-                    } elseif($thisstaff->canCloseTickets()) { ?>
-                         <label><input type="checkbox" name="reply_ticket_status" id="reply_ticket_status" value="Closed"
-                              <?php echo $statusChecked; ?>> Close on Reply</label>
-                   <?php
-                    } ?>
+                    $statusId = $info['reply_status_id'] ?: $ticket->getStatusId();
+                    $states = array('open');
+                    if ($thisstaff->canCloseTickets())
+                        $states = array_merge($states, array('closed'));
+
+                    foreach (TicketStatusList::getStatuses(
+                                array('states' => $states)) as $s) {
+                        if (!$s->isEnabled()) continue;
+                        $selected = ($statusId == $s->getId());
+                        echo sprintf('<option value="%d" %s>%s%s</option>',
+                                $s->getId(),
+                                $selected
+                                 ? 'selected="selected"' : '',
+                                __($s->getName()),
+                                $selected
+                                ? (' ('.__('current').')') : ''
+                                );
+                    }
+                    ?>
+                    </select>
                 </td>
             </tr>
-            <?php
-            } ?>
          </tbody>
         </table>
-        <p  style="padding-left:165px;">
-            <input class="btn_sm" type="submit" value="Post Reply">
-            <input class="btn_sm" type="reset" value="Reset">
+        <p  style="padding:0 165px;">
+            <input class="btn_sm" type="submit" value="<?php echo __('Post Reply');?>">
+            <input class="btn_sm" type="reset" value="<?php echo __('Reset');?>">
         </p>
     </form>
     <?php
@@ -634,100 +657,65 @@ $tcount+= $ticket->getNumNotes();
             } ?>
             <tr>
                 <td width="120" style="vertical-align:top">
-                    <label><strong>Internal Note:</strong><span class='error'>&nbsp;*</span></label>
+                    <label><strong><?php echo __('Internal Note'); ?>:</strong><span class='error'>&nbsp;*</span></label>
                 </td>
                 <td>
                     <div>
-                        <div class="faded" style="padding-left:0.15em">
-                        Note title - summary of the note (optional)</div>
+                        <div class="faded" style="padding-left:0.15em"><?php
+                        echo __('Note title - summary of the note (optional)'); ?></div>
                         <input type="text" name="title" id="title" size="60" value="<?php echo $info['title']; ?>" >
                         <br/>
                         <span class="error">&nbsp;<?php echo $errors['title']; ?></span>
                     </div>
                     <br/>
+                    <div class="error"><?php echo $errors['note']; ?></div>
                     <textarea name="note" id="internal_note" cols="80"
-                        placeholder="Note details"
+                        placeholder="<?php echo __('Note details'); ?>"
                         rows="9" wrap="soft" data-draft-namespace="ticket.note"
                         data-draft-object-id="<?php echo $ticket->getId(); ?>"
                         class="richtext ifhtml draft draft-delete"><?php echo $info['note'];
                         ?></textarea>
-                        <span class="error"><?php echo $errors['note']; ?></span>
-                        <br>
-                </td>
-            </tr>
-            <?php
-            if($cfg->allowAttachments()) { ?>
-            <tr>
-                <td width="120">
-                    <label for="attachment">Attachments:</label>
-                </td>
-                <td class="attachments">
-                    <div class="uploads">
-                    </div>
-                    <div class="file_input">
-                        <input type="file" class="multifile" name="attachments[]" size="30" value="" />
-                    </div>
+                <div class="attachments">
+<?php
+print $note_form->getField('attachments')->render();
+?>
+                </div>
                 </td>
             </tr>
-            <?php
-            }
-            ?>
             <tr><td colspan="2">&nbsp;</td></tr>
             <tr>
                 <td width="120">
-                    <label>Ticket Status:</label>
+                    <label><?php echo __('Ticket Status');?>:</label>
                 </td>
                 <td>
                     <div class="faded"></div>
-                    <select name="state">
-                        <option value="" selected="selected">&mdash; unchanged &mdash;</option>
+                    <select name="note_status_id">
                         <?php
-                        $state = $info['state'];
-                        if($ticket->isClosed()){
-                            echo sprintf('<option value="open" %s>Reopen Ticket</option>',
-                                    ($state=='reopen')?'selected="selelected"':'');
-                        } else {
-                            if($thisstaff->canCloseTickets())
-                                echo sprintf('<option value="closed" %s>Close Ticket</option>',
-                                    ($state=='closed')?'selected="selelected"':'');
-
-                            /* Ticket open - states */
-                            echo '<option value="" disabled="disabled">&mdash; Ticket States &mdash;</option>';
-
-                            //Answer - state
-                            if($ticket->isAnswered())
-                                echo sprintf('<option value="unanswered" %s>Mark As Unanswered</option>',
-                                    ($state=='unanswered')?'selected="selelected"':'');
-                            else
-                                echo sprintf('<option value="answered" %s>Mark As Answered</option>',
-                                    ($state=='answered')?'selected="selelected"':'');
-
-                            //overdue - state
-                            // Only department manager can set/clear overdue flag directly.
-                            // Staff with edit perm. can still set overdue date & change SLA.
-                            if($dept && $dept->isManager($thisstaff)) {
-                                if(!$ticket->isOverdue())
-                                    echo sprintf('<option value="overdue" %s>Flag As Overdue</option>',
-                                        ($state=='answered')?'selected="selelected"':'');
-                                else
-                                    echo sprintf('<option value="notdue" %s>Clear Overdue Flag</option>',
-                                        ($state=='notdue')?'selected="selelected"':'');
-
-                                if($ticket->isAssigned())
-                                    echo sprintf('<option value="unassigned" %s>Release (Unassign) Ticket</option>',
-                                        ($state=='unassigned')?'selected="selelected"':'');
-                            }
-                        }?>
+                        $statusId = $info['note_status_id'] ?: $ticket->getStatusId();
+                        $states = array('open');
+                        if ($thisstaff->canCloseTickets())
+                            $states = array_merge($states, array('closed'));
+                        foreach (TicketStatusList::getStatuses(
+                                    array('states' => $states)) as $s) {
+                            if (!$s->isEnabled()) continue;
+                            $selected = $statusId == $s->getId();
+                            echo sprintf('<option value="%d" %s>%s%s</option>',
+                                    $s->getId(),
+                                    $selected ? 'selected="selected"' : '',
+                                    __($s->getName()),
+                                    $selected ? (' ('.__('current').')') : ''
+                                    );
+                        }
+                        ?>
                     </select>
-                    &nbsp;<span class='error'>*&nbsp;<?php echo $errors['state']; ?></span>
-                    </div>
+                    &nbsp;<span class='error'>*&nbsp;<?php echo $errors['note_status_id']; ?></span>
                 </td>
             </tr>
         </table>
 
        <p  style="padding-left:165px;">
-           <input class="btn_sm" type="submit" value="Post Note">
-           <input class="btn_sm" type="reset" value="Reset">
+           <input class="btn_sm" type="submit" value="<?php echo __('Post Note');?>">
+           <input class="btn_sm" type="reset" value="<?php echo __('Reset');?>">
        </p>
    </form>
     <?php
@@ -748,15 +736,15 @@ $tcount+= $ticket->getNumNotes();
             } ?>
             <tr>
                 <td width="120">
-                    <label for="deptId"><strong>Department:</strong></label>
+                    <label for="deptId"><strong><?php echo __('Department');?>:</strong></label>
                 </td>
                 <td>
                     <?php
-                        echo sprintf('<span class="faded">Ticket is currently in <b>%s</b> department.</span>', $ticket->getDeptName());
+                        echo sprintf('<span class="faded">'.__('Ticket is currently in <b>%s</b> department.').'</span>', $ticket->getDeptName());
                     ?>
                     <br>
                     <select id="deptId" name="deptId">
-                        <option value="0" selected="selected">&mdash; Select Target Department &mdash;</option>
+                        <option value="0" selected="selected">&mdash; <?php echo __('Select Target Department');?> &mdash;</option>
                         <?php
                         if($depts=Dept::getDepartments()) {
                             foreach($depts as $id =>$name) {
@@ -771,11 +759,11 @@ $tcount+= $ticket->getNumNotes();
             </tr>
             <tr>
                 <td width="120" style="vertical-align:top">
-                    <label><strong>Comments:</strong><span class='error'>&nbsp;*</span></label>
+                    <label><strong><?php echo __('Comments'); ?>:</strong><span class='error'>&nbsp;*</span></label>
                 </td>
                 <td>
                     <textarea name="transfer_comments" id="transfer_comments"
-                        placeholder="Enter reasons for the transfer"
+                        placeholder="<?php echo __('Enter reasons for the transfer'); ?>"
                         class="richtext ifhtml no-bar" cols="80" rows="7" wrap="soft"><?php
                         echo $info['transfer_comments']; ?></textarea>
                     <span class="error"><?php echo $errors['transfer_comments']; ?></span>
@@ -783,8 +771,8 @@ $tcount+= $ticket->getNumNotes();
             </tr>
         </table>
         <p style="padding-left:165px;">
-           <input class="btn_sm" type="submit" value="Transfer">
-           <input class="btn_sm" type="reset" value="Reset">
+           <input class="btn_sm" type="submit" value="<?php echo __('Transfer');?>">
+           <input class="btn_sm" type="reset" value="<?php echo __('Reset');?>">
         </p>
     </form>
     <?php
@@ -808,14 +796,14 @@ $tcount+= $ticket->getNumNotes();
             } ?>
             <tr>
                 <td width="120" style="vertical-align:top">
-                    <label for="assignId"><strong>Assignee:</strong></label>
+                    <label for="assignId"><strong><?php echo __('Assignee');?>:</strong></label>
                 </td>
                 <td>
                     <select id="assignId" name="assignId">
-                        <option value="0" selected="selected">&mdash; Select Staff Member OR a Team &mdash;</option>
+                        <option value="0" selected="selected">&mdash; <?php echo __('Select an Agent OR a Team');?> &mdash;</option>
                         <?php
                         if($ticket->isOpen() && !$ticket->isAssigned())
-                            echo sprintf('<option value="%d">Claim Ticket (comments optional)</option>', $thisstaff->getId());
+                            echo sprintf('<option value="%d">'.__('Claim Ticket (comments optional)').'</option>', $thisstaff->getId());
 
                         $sid=$tid=0;
 
@@ -825,7 +813,7 @@ $tcount+= $ticket->getNumNotes();
                             $users = Staff::getAvailableStaffMembers();
 
                         if ($users) {
-                            echo '<OPTGROUP label="Staff Members ('.count($users).')">';
+                            echo '<OPTGROUP label="'.sprintf(__('Agents (%d)'), count($users)).'">';
                             $staffId=$ticket->isAssigned()?$ticket->getStaffId():0;
                             foreach($users as $id => $name) {
                                 if($staffId && $staffId==$id)
@@ -842,7 +830,7 @@ $tcount+= $ticket->getNumNotes();
                         }
 
                         if(($teams=Team::getActiveTeams())) {
-                            echo '<OPTGROUP label="Teams ('.count($teams).')">';
+                            echo '<OPTGROUP label="'.sprintf(__('Teams (%d)'), count($teams)).'">';
                             $teamId=(!$sid && $ticket->isAssigned())?$ticket->getTeamId():0;
                             foreach($teams as $id => $name) {
                                 if($teamId && $teamId==$id)
@@ -857,37 +845,37 @@ $tcount+= $ticket->getNumNotes();
                         ?>
                     </select>&nbsp;<span class='error'>*&nbsp;<?php echo $errors['assignId']; ?></span>
                     <?php
-                    if($ticket->isAssigned() && $ticket->isOpen()) {
-                        echo sprintf('<div class="faded">Ticket is currently assigned to <b>%s</b></div>',
-                                $ticket->getAssignee());
+                    if ($ticket->isAssigned() && $ticket->isOpen()) { ?>
+                        <div class="faded"><?php echo sprintf(__('Ticket is currently assigned to %s'),
+                            sprintf('<b>%s</b>', $ticket->getAssignee())); ?></div> <?php
                     } elseif ($ticket->isClosed()) { ?>
-                        <div class="faded">Assigning a closed ticket will <b>reopen</b> it!</div>
+                        <div class="faded"><?php echo __('Assigning a closed ticket will <b>reopen</b> it!'); ?></div>
                     <?php } ?>
                 </td>
             </tr>
             <tr>
                 <td width="120" style="vertical-align:top">
-                    <label><strong>Comments:</strong><span class='error'>&nbsp;*</span></label>
+                    <label><strong><?php echo __('Comments');?>:</strong><span class='error'>&nbsp;</span></label>
                 </td>
                 <td>
                     <textarea name="assign_comments" id="assign_comments"
                         cols="80" rows="7" wrap="soft"
-                        placeholder="Enter reasons for the assignment or instructions for assignee"
+                        placeholder="<?php echo __('Enter reasons for the assignment or instructions for assignee'); ?>"
                         class="richtext ifhtml no-bar"><?php echo $info['assign_comments']; ?></textarea>
                     <span class="error"><?php echo $errors['assign_comments']; ?></span><br>
                 </td>
             </tr>
         </table>
         <p  style="padding-left:165px;">
-            <input class="btn_sm" type="submit" value="<?php echo $ticket->isAssigned()?'Reassign':'Assign'; ?>">
-            <input class="btn_sm" type="reset" value="Reset">
+            <input class="btn_sm" type="submit" value="<?php echo $ticket->isAssigned()?__('Reassign'):__('Assign'); ?>">
+            <input class="btn_sm" type="reset" value="<?php echo __('Reset');?>">
         </p>
     </form>
     <?php
     } ?>
 </div>
 <div style="display:none;" class="dialog" id="print-options">
-    <h3>Ticket Print Options</h3>
+    <h3><?php echo __('Ticket Print Options');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <form action="tickets.php?id=<?php echo $ticket->getId(); ?>" method="post" id="print-form" name="print-form">
@@ -895,105 +883,74 @@ $tcount+= $ticket->getNumNotes();
         <input type="hidden" name="a" value="print">
         <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>">
         <fieldset class="notes">
-            <label for="notes">Print Notes:</label>
-            <input type="checkbox" id="notes" name="notes" value="1"> Print <b>Internal</b> Notes/Comments
+            <label class="fixed-size" for="notes"><?php echo __('Print Notes');?>:</label>
+            <input type="checkbox" id="notes" name="notes" value="1"> <?php echo __('Print <b>Internal</b> Notes/Comments');?>
         </fieldset>
         <fieldset>
-            <label for="psize">Paper Size:</label>
+            <label class="fixed-size" for="psize"><?php echo __('Paper Size');?>:</label>
             <select id="psize" name="psize">
-                <option value="">&mdash; Select Print Paper Size &mdash;</option>
+                <option value="">&mdash; <?php echo __('Select Print Paper Size');?> &mdash;</option>
                 <?php
-                  $options=array('Letter', 'Legal', 'A4', 'A3');
                   $psize =$_SESSION['PAPER_SIZE']?$_SESSION['PAPER_SIZE']:$thisstaff->getDefaultPaperSize();
-                  foreach($options as $v) {
+                  foreach(Export::$paper_sizes as $v) {
                       echo sprintf('<option value="%s" %s>%s</option>',
-                                $v,($psize==$v)?'selected="selected"':'', $v);
+                                $v,($psize==$v)?'selected="selected"':'', __($v));
                   }
                 ?>
             </select>
         </fieldset>
         <hr style="margin-top:3em"/>
         <p class="full-width">
-            <span class="buttons" style="float:left">
-                <input type="reset" value="Reset">
-                <input type="button" value="Cancel" class="close">
-            </span>
-            <span class="buttons" style="float:right">
-                <input type="submit" value="Print">
-            </span>
-         </p>
-    </form>
-    <div class="clear"></div>
-</div>
-<div style="display:none;" class="dialog" id="ticket-status">
-    <h3><?php echo sprintf('%s Ticket #%s', ($ticket->isClosed()?'Reopen':'Close'), $ticket->getNumber()); ?></h3>
-    <a class="close" href=""><i class="icon-remove-circle"></i></a>
-    <hr/>
-    <?php echo sprintf('Are you sure you want to <b>%s</b> this ticket?', $ticket->isClosed()?'REOPEN':'CLOSE'); ?>
-    <form action="tickets.php?id=<?php echo $ticket->getId(); ?>" method="post" id="status-form" name="status-form">
-        <?php csrf_token(); ?>
-        <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>">
-        <input type="hidden" name="a" value="process">
-        <input type="hidden" name="do" value="<?php echo $ticket->isClosed()?'reopen':'close'; ?>">
-        <fieldset>
-            <div style="margin-bottom:0.5em">
-            <em>Reasons for status change (internal note). Optional but highly recommended.</em>
-            </div>
-            <textarea name="ticket_status_notes" id="ticket_status_notes" cols="50" rows="5" wrap="soft"
-                style="width:100%"
-                class="richtext ifhtml no-bar"><?php echo $info['ticket_status_notes']; ?></textarea>
-        </fieldset>
-        <hr style="margin-top:1em"/>
-        <p class="full-width">
-            <span class="buttons" style="float:left">
-                <input type="reset" value="Reset">
-                <input type="button" value="Cancel" class="close">
+            <span class="buttons pull-left">
+                <input type="reset" value="<?php echo __('Reset');?>">
+                <input type="button" value="<?php echo __('Cancel');?>" class="close">
             </span>
-            <span class="buttons" style="float:right">
-                <input type="submit" value="<?php echo $ticket->isClosed()?'Reopen':'Close'; ?>">
+            <span class="buttons pull-right">
+                <input type="submit" value="<?php echo __('Print');?>">
             </span>
          </p>
     </form>
     <div class="clear"></div>
 </div>
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="claim-confirm">
-        Are you sure want to <b>claim</b> (self assign) this ticket?
+        <?php echo __('Are you sure you want to <b>claim</b> (self assign) this ticket?');?>
     </p>
     <p class="confirm-action" style="display:none;" id="answered-confirm">
-        Are you sure want to flag the ticket as <b>answered</b>?
+        <?php echo __('Are you sure you want to flag the ticket as <b>answered</b>?');?>
     </p>
     <p class="confirm-action" style="display:none;" id="unanswered-confirm">
-        Are you sure want to flag the ticket as <b>unanswered</b>?
+        <?php echo __('Are you sure you want to flag the ticket as <b>unanswered</b>?');?>
     </p>
     <p class="confirm-action" style="display:none;" id="overdue-confirm">
-        Are you sure want to flag the ticket as <font color="red"><b>overdue</b></font>?
+        <?php echo __('Are you sure you want to flag the ticket as <font color="red"><b>overdue</b></font>?');?>
     </p>
     <p class="confirm-action" style="display:none;" id="banemail-confirm">
-        Are you sure want to <b>ban</b> <?php echo $ticket->getEmail(); ?>? <br><br>
-        New tickets from the email address will be auto-rejected.
+        <?php echo sprintf(__('Are you sure you want to <b>ban</b> %s?'), $ticket->getEmail());?> <br><br>
+        <?php echo __('New tickets from the email address will be automatically rejected.');?>
     </p>
     <p class="confirm-action" style="display:none;" id="unbanemail-confirm">
-        Are you sure want to <b>remove</b> <?php echo $ticket->getEmail(); ?> from ban list?
+        <?php echo sprintf(__('Are you sure you want to <b>remove</b> %s from ban list?'), $ticket->getEmail()); ?>
     </p>
     <p class="confirm-action" style="display:none;" id="release-confirm">
-        Are you sure want to <b>unassign</b> ticket from <b><?php echo $ticket->getAssigned(); ?></b>?
+        <?php echo sprintf(__('Are you sure you want to <b>unassign</b> ticket from <b>%s</b>?'), $ticket->getAssigned()); ?>
     </p>
     <p class="confirm-action" style="display:none;" id="changeuser-confirm">
         <span id="msg_warning" style="display:block;vertical-align:top">
-        <b><?php echo Format::htmlchars($ticket->getName()); ?></b> &lt;<?php echo $ticket->getEmail(); ?>&gt;
-        <br> will no longer have access to the ticket.
+        <?php echo sprintf(Format::htmlchars(__('%s <%s> will longer have access to the ticket')),
+            '<b>'.Format::htmlchars($ticket->getName()).'</b>', Format::htmlchars($ticket->getEmail())); ?>
         </span>
-        Are you sure want to <b>change</b> ticket owner to <b><span id="newuser">this guy</span></b>?
+        <?php echo sprintf(__('Are you sure want to <b>change</b> ticket owner to %s?'),
+            '<b><span id="newuser">this guy</span></b>'); ?>
     </p>
     <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE this ticket?</strong></font>
-        <br><br>Deleted tickets CANNOT be recovered, including any associated attachments.
+        <font color="red"><strong><?php echo __('Are you sure you want to DELETE this ticket?');?></strong></font>
+        <br><br><?php echo __('Deleted data CANNOT be recovered, including any associated attachments.');?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <form action="tickets.php?id=<?php echo $ticket->getId(); ?>" method="post" id="confirm-form" name="confirm-form">
         <?php csrf_token(); ?>
         <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>">
@@ -1001,11 +958,11 @@ $tcount+= $ticket->getNumNotes();
         <input type="hidden" name="do" id="action" value="">
         <hr style="margin-top:1em"/>
         <p class="full-width">
-            <span class="buttons" style="float:left">
-                <input type="button" value="Cancel" class="close">
+            <span class="buttons pull-left">
+                <input type="button" value="<?php echo __('Cancel');?>" class="close">
             </span>
-            <span class="buttons" style="float:right">
-                <input type="submit" value="OK">
+            <span class="buttons pull-right">
+                <input type="submit" value="<?php echo __('OK');?>">
             </span>
          </p>
     </form>
diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php
index 0c43254ecc087e5b74945ecfcd1a6c43601dc150..f6c17982c7aa39e88027b0b3f46a0c171e045360 100644
--- a/include/staff/tickets.inc.php
+++ b/include/staff/tickets.inc.php
@@ -15,7 +15,7 @@ if($search) {
   if( ($_REQUEST['query'] && strlen($_REQUEST['query'])<3)
       || (!$_REQUEST['query'] && isset($_REQUEST['basic_search'])) ){ //Why do I care about this crap...
       $search=false; //Instead of an error page...default back to regular query..with no search.
-      $errors['err']='Search term must be more than 3 chars';
+      $errors['err']=__('Search term must be more than 3 chars');
       $searchTerm='';
   }
 }
@@ -28,29 +28,33 @@ $status=null;
 switch(strtolower($_REQUEST['status'])){ //Status is overloaded
     case 'open':
         $status='open';
+		$results_type=__('Open Tickets');
         break;
     case 'closed':
         $status='closed';
+		$results_type=__('Closed Tickets');
         $showassigned=true; //closed by.
         break;
     case 'overdue':
         $status='open';
         $showoverdue=true;
-        $results_type='Overdue Tickets';
+        $results_type=__('Overdue Tickets');
         break;
     case 'assigned':
         $status='open';
         $staffId=$thisstaff->getId();
-        $results_type='My Tickets';
+        $results_type=__('My Tickets');
         break;
     case 'answered':
         $status='open';
         $showanswered=true;
-        $results_type='Answered Tickets';
+        $results_type=__('Answered Tickets');
         break;
     default:
-        if(!$search && !isset($_REQUEST['advsid']))
+        if (!$search && !isset($_REQUEST['advsid'])) {
             $_REQUEST['status']=$status='open';
+            $results_type=__('Open Tickets');
+        }
 }
 
 $qwhere ='';
@@ -62,20 +66,25 @@ $qwhere ='';
 $depts=$thisstaff->getDepts();
 $qwhere =' WHERE ( '
         .'  ( ticket.staff_id='.db_input($thisstaff->getId())
-        .' AND ticket.status="open")';
+        .' AND status.state="open") ';
 
 if(!$thisstaff->showAssignedOnly())
     $qwhere.=' OR ticket.dept_id IN ('.($depts?implode(',', db_input($depts)):0).')';
 
 if(($teams=$thisstaff->getTeams()) && count(array_filter($teams)))
     $qwhere.=' OR (ticket.team_id IN ('.implode(',', db_input(array_filter($teams)))
-            .') AND ticket.status="open")';
+            .') AND status.state="open") ';
 
 $qwhere .= ' )';
 
-//STATUS
-if($status) {
-    $qwhere.=' AND ticket.status='.db_input(strtolower($status));
+//STATUS to states
+$states = array(
+    'open' => array('open'),
+    'closed' => array('closed'));
+
+if($status && isset($states[$status])) {
+    $qwhere.=' AND status.state IN (
+                '.implode(',', db_input($states[$status])).' ) ';
 }
 
 if (isset($_REQUEST['uid']) && $_REQUEST['uid']) {
@@ -86,7 +95,7 @@ if (isset($_REQUEST['uid']) && $_REQUEST['uid']) {
 
 //Queues: Overloaded sub-statuses  - you've got to just have faith!
 if($staffId && ($staffId==$thisstaff->getId())) { //My tickets
-    $results_type='Assigned Tickets';
+    $results_type=__('Assigned Tickets');
     $qwhere.=' AND ticket.staff_id='.db_input($staffId);
     $showassigned=false; //My tickets...already assigned to the staff.
 }elseif($showoverdue) { //overdue
@@ -109,6 +118,7 @@ if($staffId && ($staffId==$thisstaff->getId())) { //My tickets
 
 //Search?? Somebody...get me some coffee
 $deep_search=false;
+$order_by=$order=null;
 if($search):
     $qstr.='&a='.urlencode($_REQUEST['a']);
     $qstr.='&t='.urlencode($_REQUEST['t']);
@@ -129,9 +139,12 @@ if($search):
             require_once(INCLUDE_DIR.'ajax.tickets.php');
 
             $tickets = TicketsAjaxApi::_search(array('query'=>$queryterm));
-            if (count($tickets))
-                $qwhere .= ' AND ticket.ticket_id IN ('.
-                    implode(',',db_input($tickets)).')';
+            if (count($tickets)) {
+                $ticket_ids = implode(',',db_input($tickets));
+                $qwhere .= ' AND ticket.ticket_id IN ('.$ticket_ids.')';
+                $order_by = 'FIELD(ticket.ticket_id, '.$ticket_ids.')';
+                $order = ' ';
+            }
             else
                 // No hits -- there should be an empty list of results
                 $qwhere .= ' AND false';
@@ -141,21 +154,23 @@ if($search):
 endif;
 
 if ($_REQUEST['advsid'] && isset($_SESSION['adv_'.$_REQUEST['advsid']])) {
+    $ticket_ids = implode(',', db_input($_SESSION['adv_'.$_REQUEST['advsid']]));
     $qstr.='advsid='.$_REQUEST['advsid'];
-    $qwhere .= ' AND ticket.ticket_id IN ('. implode(',',
-        db_input($_SESSION['adv_'.$_REQUEST['advsid']])).')';
+    $qwhere .= ' AND ticket.ticket_id IN ('.$ticket_ids.')';
+    // Thanks, http://stackoverflow.com/a/1631794
+    $order_by = 'FIELD(ticket.ticket_id, '.$ticket_ids.')';
+    $order = ' ';
 }
 
 $sortOptions=array('date'=>'effective_date','ID'=>'ticket.`number`*1',
     'pri'=>'pri.priority_urgency','name'=>'user.name','subj'=>'cdata.subject',
-    'status'=>'ticket.status','assignee'=>'assigned','staff'=>'staff',
+    'status'=>'status.name','assignee'=>'assigned','staff'=>'staff',
     'dept'=>'dept.dept_name');
 
 $orderWays=array('DESC'=>'DESC','ASC'=>'ASC');
 
 //Sorting options...
 $queue = isset($_REQUEST['status'])?strtolower($_REQUEST['status']):$status;
-$order_by=$order=null;
 if($_REQUEST['sort'] && $sortOptions[$_REQUEST['sort']])
     $order_by =$sortOptions[$_REQUEST['sort']];
 elseif($sortOptions[$_SESSION[$queue.'_tickets']['sort']]) {
@@ -200,10 +215,12 @@ if($_GET['limit'])
 
 $qselect ='SELECT ticket.ticket_id,tlock.lock_id,ticket.`number`,ticket.dept_id,ticket.staff_id,ticket.team_id '
     .' ,user.name'
-    .' ,email.address as email, dept.dept_name '
-         .' ,ticket.status,ticket.source,ticket.isoverdue,ticket.isanswered,ticket.created ';
+    .' ,email.address as email, dept.dept_name, status.state '
+         .' ,status.name as status,ticket.source,ticket.isoverdue,ticket.isanswered,ticket.created ';
 
 $qfrom=' FROM '.TICKET_TABLE.' ticket '.
+       ' LEFT JOIN '.TICKET_STATUS_TABLE. ' status
+            ON (status.id = ticket.status_id) '.
        ' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id'.
        ' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id'.
        ' LEFT JOIN '.DEPT_TABLE.' dept ON ticket.dept_id=dept.dept_id ';
@@ -233,7 +250,7 @@ $qselect.=' ,IF(ticket.duedate IS NULL,IF(sla.id IS NULL, NULL, DATE_ADD(ticket.
          .' ,ticket.created as ticket_created, CONCAT_WS(" ", staff.firstname, staff.lastname) as staff, team.name as team '
          .' ,IF(staff.staff_id IS NULL,team.name,CONCAT_WS(" ", staff.lastname, staff.firstname)) as assigned '
          .' ,IF(ptopic.topic_pid IS NULL, topic.topic, CONCAT_WS(" / ", ptopic.topic, topic.topic)) as helptopic '
-         .' ,cdata.priority_id, cdata.subject, pri.priority_desc, pri.priority_color';
+         .' ,cdata.priority as priority_id, cdata.subject, pri.priority_desc, pri.priority_color';
 
 $qfrom.=' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock ON (ticket.ticket_id=tlock.ticket_id AND tlock.expire>NOW()
                AND tlock.staff_id!='.db_input($thisstaff->getId()).') '
@@ -243,7 +260,7 @@ $qfrom.=' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock ON (ticket.ticket_id=tlock.ticke
        .' LEFT JOIN '.TOPIC_TABLE.' topic ON (ticket.topic_id=topic.topic_id) '
        .' LEFT JOIN '.TOPIC_TABLE.' ptopic ON (ptopic.topic_id=topic.topic_pid) '
        .' LEFT JOIN '.TABLE_PREFIX.'ticket__cdata cdata ON (cdata.ticket_id = ticket.ticket_id) '
-       .' LEFT JOIN '.PRIORITY_TABLE.' pri ON (pri.priority_id = cdata.priority_id)';
+       .' LEFT JOIN '.PRIORITY_TABLE.' pri ON (pri.priority_id = cdata.priority)';
 
 TicketForm::ensureDynamicDataView();
 
@@ -252,12 +269,13 @@ $query="$qselect $qfrom $qwhere ORDER BY $order_by $order LIMIT ".$pageNav->getS
 $hash = md5($query);
 $_SESSION['search_'.$hash] = $query;
 $res = db_query($query);
-$showing=db_num_rows($res)?$pageNav->showing():"";
+$showing=db_num_rows($res)? ' &mdash; '.$pageNav->showing():"";
 if(!$results_type)
-    $results_type = ucfirst($status).' Tickets';
+    $results_type = sprintf(__('%s Tickets' /* %s will be a status such as 'open' */),
+        mb_convert_case($status, MB_CASE_TITLE));
 
 if($search)
-    $results_type.= ' (Search Results)';
+    $results_type.= ' ('.__('Search Results').')';
 
 $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting..
 
@@ -297,23 +315,44 @@ if ($results) {
         <tr>
             <td><input type="text" id="basic-ticket-search" name="query" size=30 value="<?php echo Format::htmlchars($_REQUEST['query']); ?>"
                 autocomplete="off" autocorrect="off" autocapitalize="off"></td>
-            <td><input type="submit" name="basic_search" class="button" value="Search"></td>
-            <td>&nbsp;&nbsp;<a href="#" id="go-advanced">[advanced]</a>&nbsp;<i class="help-tip icon-question-sign" href="#advanced"></i></td>
+            <td><input type="submit" name="basic_search" class="button" value="<?php echo __('Search'); ?>"></td>
+            <td>&nbsp;&nbsp;<a href="#" id="go-advanced">[<?php echo __('advanced'); ?>]</a>&nbsp;<i class="help-tip icon-question-sign" href="#advanced"></i></td>
         </tr>
     </table>
     </form>
 </div>
 <!-- SEARCH FORM END -->
 <div class="clear"></div>
-<div style="margin-bottom:20px">
-<form action="tickets.php" method="POST" name='tickets'>
+<div style="margin-bottom:20px; padding-top:10px;">
+<div>
+        <div class="pull-left flush-left">
+            <h2><a href="<?php echo Format::htmlchars($_SERVER['REQUEST_URI']); ?>"
+                title="<?php echo __('Refresh'); ?>"><i class="icon-refresh"></i> <?php echo
+                $results_type.$showing; ?></a></h2>
+        </div>
+        <div class="pull-right flush-right">
+
+            <?php
+            if ($thisstaff->canDeleteTickets()) { ?>
+            <a id="tickets-delete" class="action-button pull-right tickets-action"
+                href="#tickets/status/delete"><i
+            class="icon-trash"></i> <?php echo __('Delete'); ?></a>
+            <?php
+            } ?>
+            <?php
+            if ($thisstaff->canManageTickets()) {
+                echo TicketStatus::status_options();
+            }
+            ?>
+        </div>
+</div>
+<div class="clear" style="margin-bottom:10px;"></div>
+<form action="tickets.php" method="POST" name='tickets' id="tickets">
 <?php csrf_token(); ?>
- <a class="refresh" href="<?php echo Format::htmlchars($_SERVER['REQUEST_URI']); ?>">Refresh</a>
  <input type="hidden" name="a" value="mass_process" >
  <input type="hidden" name="do" id="action" value="" >
  <input type="hidden" name="status" value="<?php echo Format::htmlchars($_REQUEST['status']); ?>" >
  <table class="list" border="0" cellspacing="1" cellpadding="2" width="940">
-    <caption><?php echo $showing; ?>&nbsp;&nbsp;&nbsp;<?php echo $results_type; ?></caption>
     <thead>
         <tr>
             <?php if($thisstaff->canManageTickets()) { ?>
@@ -321,26 +360,26 @@ if ($results) {
             <?php } ?>
 	        <th width="70">
                 <a <?php echo $id_sort; ?> href="tickets.php?sort=ID&order=<?php echo $negorder; ?><?php echo $qstr; ?>"
-                    title="Sort By Ticket ID <?php echo $negorder; ?>">Ticket</a></th>
+                    title="<?php echo sprintf(__('Sort by %s %s'), __('Ticket ID'), __($negorder)); ?>"><?php echo __('Ticket'); ?></a></th>
 	        <th width="70">
                 <a  <?php echo $date_sort; ?> href="tickets.php?sort=date&order=<?php echo $negorder; ?><?php echo $qstr; ?>"
-                    title="Sort By Date <?php echo $negorder; ?>">Date</a></th>
+                    title="<?php echo sprintf(__('Sort by %s %s'), __('Date'), __($negorder)); ?>"><?php echo __('Date'); ?></a></th>
 	        <th width="280">
                  <a <?php echo $subj_sort; ?> href="tickets.php?sort=subj&order=<?php echo $negorder; ?><?php echo $qstr; ?>"
-                    title="Sort By Subject <?php echo $negorder; ?>">Subject</a></th>
+                    title="<?php echo sprintf(__('Sort by %s %s'), __('Subject'), __($negorder)); ?>"><?php echo __('Subject'); ?></a></th>
             <th width="170">
                 <a <?php echo $name_sort; ?> href="tickets.php?sort=name&order=<?php echo $negorder; ?><?php echo $qstr; ?>"
-                     title="Sort By Name <?php echo $negorder; ?>">From</a></th>
+                     title="<?php echo sprintf(__('Sort by %s %s'), __('Name'), __($negorder)); ?>"><?php echo __('From');?></a></th>
             <?php
             if($search && !$status) { ?>
                 <th width="60">
                     <a <?php echo $status_sort; ?> href="tickets.php?sort=status&order=<?php echo $negorder; ?><?php echo $qstr; ?>"
-                        title="Sort By Status <?php echo $negorder; ?>">Status</a></th>
+                        title="<?php echo sprintf(__('Sort by %s %s'), __('Status'), __($negorder)); ?>"><?php echo __('Status');?></a></th>
             <?php
             } else { ?>
                 <th width="60" <?php echo $pri_sort;?>>
                     <a <?php echo $pri_sort; ?> href="tickets.php?sort=pri&order=<?php echo $negorder; ?><?php echo $qstr; ?>"
-                        title="Sort By Priority <?php echo $negorder; ?>">Priority</a></th>
+                        title="<?php echo sprintf(__('Sort by %s %s'), __('Priority'), __($negorder)); ?>"><?php echo __('Priority');?></a></th>
             <?php
             }
 
@@ -349,18 +388,18 @@ if ($results) {
                 if(!strcasecmp($status,'closed')) { ?>
                     <th width="150">
                         <a <?php echo $staff_sort; ?> href="tickets.php?sort=staff&order=<?php echo $negorder; ?><?php echo $qstr; ?>"
-                            title="Sort By Closing Staff Name <?php echo $negorder; ?>">Closed By</a></th>
+                            title="<?php echo sprintf(__('Sort by %s %s'), __("Closing Agent's Name"), __($negorder)); ?>"><?php echo __('Closed By'); ?></a></th>
                 <?php
                 } else { //assigned to ?>
                     <th width="150">
                         <a <?php echo $assignee_sort; ?> href="tickets.php?sort=assignee&order=<?php echo $negorder; ?><?php echo $qstr; ?>"
-                            title="Sort By Assignee <?php echo $negorder;?>">Assigned To</a></th>
+                            title="<?php echo sprintf(__('Sort by %s %s'), __('Assignee'), __($negorder)); ?>"><?php echo __('Assigned To'); ?></a></th>
                 <?php
                 }
             } else { ?>
                 <th width="150">
                     <a <?php echo $dept_sort; ?> href="tickets.php?sort=dept&order=<?php echo $negorder;?><?php echo $qstr; ?>"
-                        title="Sort By Department <?php echo $negorder; ?>">Department</a></th>
+                        title="<?php echo sprintf(__('Sort by %s %s'), __('Department'), __($negorder)); ?>"><?php echo __('Department');?></a></th>
             <?php
             } ?>
         </tr>
@@ -393,7 +432,7 @@ if ($results) {
                 $tid=$row['number'];
                 $subject = Format::htmlchars(Format::truncate($row['subject'],40));
                 $threadcount=$row['thread_count'];
-                if(!strcasecmp($row['status'],'open') && !$row['isanswered'] && !$row['lock_id']) {
+                if(!strcasecmp($row['state'],'open') && !$row['isanswered'] && !$row['lock_id']) {
                     $tid=sprintf('<b>%s</b>',$tid);
                 }
                 ?>
@@ -405,10 +444,11 @@ if ($results) {
                         $sel=true;
                     ?>
                 <td align="center" class="nohover">
-                    <input class="ckb" type="checkbox" name="tids[]" value="<?php echo $row['ticket_id']; ?>" <?php echo $sel?'checked="checked"':''; ?>>
+                    <input class="ckb" type="checkbox" name="tids[]"
+                        value="<?php echo $row['ticket_id']; ?>" <?php echo $sel?'checked="checked"':''; ?>>
                 </td>
                 <?php } ?>
-                <td align="center" title="<?php echo $row['email']; ?>" nowrap>
+                <td title="<?php echo $row['email']; ?>" nowrap>
                   <a class="Icon <?php echo strtolower($row['source']); ?>Ticket ticketPreview" title="Preview Ticket"
                     href="tickets.php?id=<?php echo $row['ticket_id']; ?>"><?php echo $tid; ?></a></td>
                 <td align="center" nowrap><?php echo Format::db_datetime($row['effective_date']); ?></td>
@@ -429,7 +469,7 @@ if ($results) {
                 <?php
                 if($search && !$status){
                     $displaystatus=ucfirst($row['status']);
-                    if(!strcasecmp($row['status'],'open'))
+                    if(!strcasecmp($row['state'],'open'))
                         $displaystatus="<b>$displaystatus</b>";
                     echo "<td>$displaystatus</td>";
                 } else { ?>
@@ -443,20 +483,20 @@ if ($results) {
             <?php
             } //end of while.
         else: //not tickets found!! set fetch error.
-            $ferror='There are no tickets here. (Leave a little early today).';
+            $ferror=__('There are no tickets matching your criteria.');
         endif; ?>
     </tbody>
     <tfoot>
      <tr>
         <td colspan="7">
             <?php if($res && $num && $thisstaff->canManageTickets()){ ?>
-            Select:&nbsp;
-            <a id="selectAll" href="#ckb">All</a>&nbsp;&nbsp;
-            <a id="selectNone" href="#ckb">None</a>&nbsp;&nbsp;
-            <a id="selectToggle" href="#ckb">Toggle</a>&nbsp;&nbsp;
+            <?php echo __('Select');?>:&nbsp;
+            <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
+            <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
+            <a id="selectToggle" href="#ckb"><?php echo __('Toggle');?></a>&nbsp;&nbsp;
             <?php }else{
                 echo '<i>';
-                echo $ferror?Format::htmlchars($ferror):'Query returned 0 results.';
+                echo $ferror?Format::htmlchars($ferror):__('Query returned 0 results.');
                 echo '</i>';
             } ?>
         </td>
@@ -465,104 +505,59 @@ if ($results) {
     </table>
     <?php
     if($num>0){ //if we actually had any tickets returned.
-        echo '<div>&nbsp;Page:'.$pageNav->getPageLinks().'&nbsp;';
+        echo '<div>&nbsp;'.__('Page').':'.$pageNav->getPageLinks().'&nbsp;';
         echo '<a class="export-csv no-pjax" href="?a=export&h='
-            .$hash.'&status='.$_REQUEST['status'] .'">Export</a>&nbsp;<i class="help-tip icon-question-sign" href="#export"></i></div>';
-    ?>
-        <?php
-         if($thisstaff->canManageTickets()) { ?>
-           <p class="centered" id="actions">
-            <?php
-            $status=$_REQUEST['status']?$_REQUEST['status']:$status;
-            switch (strtolower($status)) {
-                case 'closed': ?>
-                    <input class="button" type="submit" name="reopen" value="Reopen" >
-                    <?php
-                    break;
-                case 'open':
-                case 'answered':
-                case 'assigned':
-                    ?>
-                    <input class="button" type="submit" name="mark_overdue" value="Overdue" >
-                    <input class="button" type="submit" name="close" value="Close">
-                    <?php
-                    break;
-                case 'overdue':
-                    ?>
-                    <input class="button" type="submit" name="close" value="Close">
-                    <?php
-                    break;
-                default: //search??
-                    ?>
-                    <input class="button" type="submit" name="close" value="Close" >
-                    <input class="button" type="submit" name="reopen" value="Reopen">
-            <?php
-            }
-            if($thisstaff->canDeleteTickets()) { ?>
-                <input class="button" type="submit" name="delete" value="Delete">
-            <?php } ?>
-        </p>
-        <?php
-       }
+            .$hash.'&status='.$_REQUEST['status'] .'">'.__('Export').'</a>&nbsp;<i class="help-tip icon-question-sign" href="#export"></i></div>';
     } ?>
     </form>
 </div>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
-    <p class="confirm-action" style="display:none;" id="close-confirm">
-        Are you sure want to <b>close</b> selected open tickets?
-    </p>
-    <p class="confirm-action" style="display:none;" id="reopen-confirm">
-        Are you sure want to <b>reopen</b> selected closed tickets?
-    </p>
     <p class="confirm-action" style="display:none;" id="mark_overdue-confirm">
-        Are you sure want to flag the selected tickets as <font color="red"><b>overdue</b></font>?
-    </p>
-    <p class="confirm-action" style="display:none;" id="delete-confirm">
-        <font color="red"><strong>Are you sure you want to DELETE selected tickets?</strong></font>
-        <br><br>Deleted tickets CANNOT be recovered, including any associated attachments.
+        <?php echo __('Are you sure want to flag the selected tickets as <font color="red"><b>overdue</b></font>?');?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.');?></div>
     <hr style="margin-top:1em"/>
     <p class="full-width">
-        <span class="buttons" style="float:left">
-            <input type="button" value="No, Cancel" class="close">
+        <span class="buttons pull-left">
+            <input type="button" value="<?php echo __('No, Cancel');?>" class="close">
         </span>
-        <span class="buttons" style="float:right">
-            <input type="button" value="Yes, Do it!" class="confirm">
+        <span class="buttons pull-right">
+            <input type="button" value="<?php echo __('Yes, Do it!');?>" class="confirm">
         </span>
      </p>
     <div class="clear"></div>
 </div>
 
 <div class="dialog" style="display:none;" id="advanced-search">
-    <h3>Advanced Ticket Search</h3>
+    <h3><?php echo __('Advanced Ticket Search');?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
+    <hr/>
     <form action="tickets.php" method="post" id="search" name="search">
         <input type="hidden" name="a" value="search">
         <fieldset class="query">
-            <label for="query">Keyword:</label>
-            <input type="input" id="query" name="query" size="20"> <em>Optional</em>
+            <input type="input" id="query" name="query" size="20" placeholder="<?php echo __('Keywords') . ' &mdash; ' . __('Optional'); ?>">
         </fieldset>
-        <fieldset>
-            <label for="status">Status:</label>
-            <select id="status" name="status">
-                <option value="">&mdash; Any Status &mdash;</option>
-                <option value="open">Open</option>
+        <fieldset class="span6">
+            <label for="statusId"><?php echo __('Statuses');?>:</label>
+            <select id="statusId" name="statusId">
+                 <option value="">&mdash; <?php echo __('Any Status');?> &mdash;</option>
                 <?php
-                if(!$cfg->showAnsweredTickets()) {?>
-                <option value="answered">Answered</option>
-                <?php
-                } ?>
-                <option value="overdue">Overdue</option>
-                <option value="closed">Closed</option>
+                foreach (TicketStatusList::getStatuses(
+                            array('states' => array('open', 'closed'))) as $s) {
+                    echo sprintf('<option data-state="%s" value="%d">%s</option>',
+                            $s->getState(), $s->getId(), __($s->getName()));
+                }
+                ?>
             </select>
-            <label for="deptId">Dept:</label>
+        </fieldset>
+        <fieldset class="span6">
+            <label for="deptId"><?php echo __('Departments');?>:</label>
             <select id="deptId" name="deptId">
-                <option value="">&mdash; All Departments &mdash;</option>
+                <option value="">&mdash; <?php echo __('All Departments');?> &mdash;</option>
                 <?php
                 if(($mydepts = $thisstaff->getDepts()) && ($depts=Dept::getDepartments())) {
                     foreach($depts as $id =>$name) {
@@ -573,15 +568,27 @@ if ($results) {
                 ?>
             </select>
         </fieldset>
-        <fieldset class="owner">
-            <label for="assignee">Assigned To:</label>
+        <fieldset class="span6">
+            <label for="flag"><?php echo __('Flags');?>:</label>
+            <select id="flag" name="flag">
+                 <option value="">&mdash; <?php echo __('Any Flags');?> &mdash;</option>
+                 <?php
+                 if (!$cfg->showAnsweredTickets()) { ?>
+                 <option data-state="open" value="answered"><?php echo __('Answered');?></option>
+                 <?php
+                 } ?>
+                 <option data-state="open" value="overdue"><?php echo __('Overdue');?></option>
+            </select>
+        </fieldset>
+        <fieldset class="owner span6">
+            <label for="assignee"><?php echo __('Assigned To');?>:</label>
             <select id="assignee" name="assignee">
-                <option value="">&mdash; Anyone &mdash;</option>
-                <option value="0">&mdash; Unassigned &mdash;</option>
-                <option value="<?php echo $thisstaff->getId(); ?>">Me</option>
+                <option value="">&mdash; <?php echo __('Anyone');?> &mdash;</option>
+                <option value="0">&mdash; <?php echo __('Unassigned');?> &mdash;</option>
+                <option value="s<?php echo $thisstaff->getId(); ?>"><?php echo __('Me');?></option>
                 <?php
                 if(($users=Staff::getStaffMembers())) {
-                    echo '<OPTGROUP label="Staff Members ('.count($users).')">';
+                    echo '<OPTGROUP label="'.sprintf(__('Agents (%d)'),count($users)).'">';
                     foreach($users as $id => $name) {
                         $k="s$id";
                         echo sprintf('<option value="%s">%s</option>', $k, $name);
@@ -590,7 +597,7 @@ if ($results) {
                 }
 
                 if(($teams=Team::getTeams())) {
-                    echo '<OPTGROUP label="Teams ('.count($teams).')">';
+                    echo '<OPTGROUP label="'.__('Teams').' ('.count($teams).')">';
                     foreach($teams as $id => $name) {
                         $k="t$id";
                         echo sprintf('<option value="%s">%s</option>', $k, $name);
@@ -599,22 +606,11 @@ if ($results) {
                 }
                 ?>
             </select>
-            <label for="staffId">Closed By:</label>
-            <select id="staffId" name="staffId">
-                <option value="0">&mdash; Anyone &mdash;</option>
-                <option value="<?php echo $thisstaff->getId(); ?>">Me</option>
-                <?php
-                if(($users=Staff::getStaffMembers())) {
-                    foreach($users as $id => $name)
-                        echo sprintf('<option value="%d">%s</option>', $id, $name);
-                }
-                ?>
-            </select>
         </fieldset>
-        <fieldset>
-            <label for="topicId">Help Topic:</label>
+        <fieldset class="span6">
+            <label for="topicId"><?php echo __('Help Topics');?>:</label>
             <select id="topicId" name="topicId">
-                <option value="" selected >&mdash; All Help Topics &mdash;</option>
+                <option value="" selected >&mdash; <?php echo __('All Help Topics');?> &mdash;</option>
                 <?php
                 if($topics=Topic::getHelpTopics()) {
                     foreach($topics as $id =>$name)
@@ -623,35 +619,70 @@ if ($results) {
                 ?>
             </select>
         </fieldset>
+        <fieldset class="owner span6">
+            <label for="staffId"><?php echo __('Closed By');?>:</label>
+            <select id="staffId" name="staffId">
+                <option value="0">&mdash; <?php echo __('Anyone');?> &mdash;</option>
+                <option value="<?php echo $thisstaff->getId(); ?>"><?php echo __('Me');?></option>
+                <?php
+                if(($users=Staff::getStaffMembers())) {
+                    foreach($users as $id => $name)
+                        echo sprintf('<option value="%d">%s</option>', $id, $name);
+                }
+                ?>
+            </select>
+        </fieldset>
         <fieldset class="date_range">
-            <label>Date Range:</label>
+            <label><?php echo __('Date Range').' &mdash; '.__('Create Date');?>:</label>
             <input class="dp" type="input" size="20" name="startDate">
-            <span>TO</span>
+            <span class="between"><?php echo __('TO');?></span>
             <input class="dp" type="input" size="20" name="endDate">
         </fieldset>
-        <fieldset>
         <?php
-        foreach (TicketForm::getInstance()->getFields() as $f) {
-            if (in_array($f->get('type'), array('text', 'memo', 'phone', 'thread')))
+        $tform = TicketForm::objects()->one();
+        echo $tform->getForm()->getMedia();
+        foreach ($tform->getInstance()->getFields() as $f) {
+            if (!$f->hasData())
                 continue;
-            elseif (!$f->hasData())
+            elseif (!$f->getImpl()->hasSpecialSearch())
                 continue;
-            ?><label><?php echo $f->getLabel(); ?>:</label>
-                <div style="display:inline-block;width: 12.5em;"><?php
+            ?><fieldset class="span6">
+            <label><?php echo $f->getLabel(); ?>:</label><div><?php
                      $f->render('search'); ?></div>
+            </fieldset>
         <?php } ?>
-        </fieldset>
+        <hr/>
+        <div id="result-count" class="clear"></div>
         <p>
-            <span class="buttons">
-                <input type="submit" value="Search">
-                <input type="reset" value="Reset">
-                <input type="button" value="Cancel" class="close">
+            <span class="buttons pull-right">
+                <input type="submit" value="<?php echo __('Search');?>">
+            </span>
+            <span class="buttons pull-left">
+                <input type="reset" value="<?php echo __('Reset');?>">
+                <input type="button" value="<?php echo __('Cancel');?>" class="close">
             </span>
             <span class="spinner">
                 <img src="./images/ajax-loader.gif" width="16" height="16">
             </span>
         </p>
     </form>
-    <div id="result-count">
-    </div>
 </div>
+<script type="text/javascript">
+$(function() {
+    $(document).off('.tickets');
+    $(document).on('click.tickets', 'a.tickets-action', function(e) {
+        e.preventDefault();
+        var count = checkbox_checker($('form#tickets'), 1);
+        if (count) {
+            var url = 'ajax.php/'
+            +$(this).attr('href').substr(1)
+            +'?count='+count
+            +'&_uid='+new Date().getTime();
+            $.dialog(url, [201], function (xhr) {
+                window.location.href = window.location.href;
+             });
+        }
+        return false;
+    });
+});
+</script>
diff --git a/include/staff/tpl.inc.php b/include/staff/tpl.inc.php
index 139049c0abc318e0279694d03b3af587f636aa47..fe048decd06309b9f2f3383aa3950bb0bcbed687 100644
--- a/include/staff/tpl.inc.php
+++ b/include/staff/tpl.inc.php
@@ -35,13 +35,14 @@ $tpl=$msgtemplates[$selected];
 
 ?>
 <form method="get" action="templates.php?">
-<h2><span>Email Template Set &nbsp;/&nbsp; <span><a href="templates.php?tpl_id=<?php echo $tpl_id; ?>"><?php echo $name; ?></a>
+<h2><span><?php echo __('Email Template Set');
+    ?> &nbsp;/&nbsp; <span><a href="templates.php?tpl_id=<?php echo $tpl_id; ?>"><?php echo $name; ?></a>
     <input type="hidden" name="a" value="manage">
     <input type="hidden" name="tpl_id" value="<?php echo $tpl_id; ?>">
 <div class="pull-right">
-    <span style="font-size:10pt">Viewing:</span>
+    <span style="font-size:10pt"><?php echo __('Viewing'); ?>:</span>
     <select id="tpl_options" name="id" style="width:300px;">
-        <option value="">&mdash; Select Setting Group &mdash;</option>
+        <option value="">&mdash; <?php echo __('Select Setting Group'); ?> &mdash;</option>
         <?php
         $impl = $group->getTemplates();
         $current_group = false;
@@ -58,12 +59,12 @@ $tpl=$msgtemplates[$selected];
                     echo "</optgroup>";
                 $current_group = $nfo['group']; ?>
                 <optgroup label="<?php echo isset($_groups[$current_group])
-                    ? $_groups[$current_group] : $current_group; ?>">
+                    ? __($_groups[$current_group]) : $current_group; ?>">
             <?php }
             $sel=($selected==$cn)?'selected="selected"':'';
             echo sprintf('<option value="%s" %s>%s</option>',
                 isset($impl[$cn]) ? $impl[$cn]->getId() : $cn,
-                $sel,$nfo['name']);
+                $sel,__($nfo['name']));
         }
         if ($current_group)
             echo "</optgroup>";
@@ -85,20 +86,20 @@ $tpl=$msgtemplates[$selected];
 
 <div style="border:1px solid #ccc;background:#f0f0f0;padding:5px 10px;
     margin:10px 0;">
-<h3 style="font-size:12pt;margin:0"><?php echo $desc['name']; ?>
+<h3 style="font-size:12pt;margin:0"><?php echo __($desc['name']); ?>
     &nbsp;<i class="help-tip icon-question-sign"
-        data-content="<?php echo Format::htmlchars($desc['desc']); ?>"
-        data-title="<?php echo Format::htmlchars($desc['name']); ?>"></i>
+        data-content="<?php echo Format::htmlchars(__($desc['desc'])); ?>"
+        data-title="<?php echo Format::htmlchars(__($desc['name'])); ?>"></i>
     <a style="font-size:10pt" class="tip pull-right" href="#ticket_variables.txt">
     <i class="icon-tags"></i>
-    Supported Variables</a>
+    <?php echo __('Supported Variables'); ?></a>
     </h3>
 <?php if ($errors) { ?>
     <font class="error"><?php echo $errors['subject']; ?>&nbsp;<?php echo $errors['body']; ?></font>
 <?php } ?>
 </div>
 
-<div style="padding-bottom:3px;" class="faded"><strong>Email Subject and Body:</strong></div>
+<div style="padding-bottom:3px;" class="faded"><strong><?php echo __('Email Subject and Body'); ?>:</strong></div>
 <div id="toolbar"></div>
 <div id="save" style="padding-top:5px;">
     <input type="text" name="subject" size="65" value="<?php echo $info['subject']; ?>"
@@ -113,10 +114,10 @@ $tpl=$msgtemplates[$selected];
 </div>
 
 <p style="text-align:center">
-    <input class="button" type="submit" name="submit" value="Save Changes">
-    <input class="button" type="reset" name="reset" value="Reset Changes" onclick="javascript:
+    <input class="button" type="submit" name="submit" value="<?php echo __('Save Changes'); ?>">
+    <input class="button" type="reset" name="reset" value="<?php echo __('Reset Changes'); ?>" onclick="javascript:
         setTimeout('location.reload()', 25);" />
-    <input class="button" type="button" name="cancel" value="Cancel Changes"
+    <input class="button" type="button" name="cancel" value="<?php echo __('Cancel Changes'); ?>"
         onclick='window.location.href="templates.php?tpl_id=<?php echo $tpl_id; ?>"'>
 </p>
 </form>
diff --git a/include/staff/user-view.inc.php b/include/staff/user-view.inc.php
index 65b47c7d1ac41b25b5fc48fc1ce626627c607809..58f4348ee258fe3d26c1fefe8d196083cb6c2328 100644
--- a/include/staff/user-view.inc.php
+++ b/include/staff/user-view.inc.php
@@ -13,20 +13,23 @@ $org = $user->getOrganization();
              title="Reload"><i class="icon-refresh"></i> <?php echo Format::htmlchars($user->getName()); ?></a></h2>
         </td>
         <td width="50%" class="right_align has_bottom_border">
-            <span class="action-button" data-dropdown="#action-dropdown-more">
-                <span ><i class="icon-cog"></i> More</span>
-                <i class="icon-caret-down"></i>
+            <span class="action-button pull-right" data-dropdown="#action-dropdown-more">
+                <i class="icon-caret-down pull-right"></i>
+                <span ><i class="icon-cog"></i> <?php echo __('More'); ?></span>
             </span>
-            <a id="user-delete" class="action-button user-action"
-            href="#users/<?php echo $user->getId(); ?>/delete"><i class="icon-trash"></i> Delete User</a>
+            <a id="user-delete" class="action-button pull-right user-action"
+            href="#users/<?php echo $user->getId(); ?>/delete"><i class="icon-trash"></i>
+            <?php echo __('Delete User'); ?></a>
             <?php
             if ($account) { ?>
-            <a id="user-manage" class="action-button user-action"
-            href="#users/<?php echo $user->getId(); ?>/manage"><i class="icon-edit"></i> Manage Account</a>
+            <a id="user-manage" class="action-button pull-right user-action"
+            href="#users/<?php echo $user->getId(); ?>/manage"><i class="icon-edit"></i>
+            <?php echo __('Manage Account'); ?></a>
             <?php
             } else { ?>
-            <a id="user-register" class="action-button user-action"
-            href="#users/<?php echo $user->getId(); ?>/register"><i class="icon-edit"></i> Register</a>
+            <a id="user-register" class="action-button pull-right user-action"
+            href="#users/<?php echo $user->getId(); ?>/register"><i class="icon-edit"></i>
+            <?php echo __('Register'); ?></a>
             <?php
             } ?>
             <div id="action-dropdown-more" class="action-dropdown anchor-right">
@@ -36,16 +39,19 @@ $org = $user->getOrganization();
                     if (!$account->isConfirmed()) {
                         ?>
                     <li><a class="confirm-action" href="#confirmlink"><i
-                        class="icon-envelope"></i> Send Activation Email</a></li>
+                        class="icon-envelope"></i>
+                        <?php echo __('Send Activation Email'); ?></a></li>
                     <?php
                     } else { ?>
                     <li><a class="confirm-action" href="#pwreset"><i
-                        class="icon-envelope"></i> Send Password Reset Email</a></li>
+                        class="icon-envelope"></i>
+                        <?php echo __('Send Password Reset Email'); ?></a></li>
                     <?php
                     } ?>
                     <li><a class="user-action"
                         href="#users/<?php echo $user->getId(); ?>/manage/access"><i
-                        class="icon-lock"></i> Manage Account Access</a></li>
+                        class="icon-lock"></i>
+                        <?php echo __('Manage Account Access'); ?></a></li>
                 <?php
 
                 } ?>
@@ -53,7 +59,8 @@ $org = $user->getOrganization();
                     ?>/forms/manage" onclick="javascript:
                     $.dialog($(this).attr('href').substr(1), 201);
                     return false"
-                    ><i class="icon-paste"></i> Manage Forms</a></li>
+                    ><i class="icon-paste"></i>
+                    <?php echo __('Manage Forms'); ?></a></li>
 
               </ul>
             </div>
@@ -65,7 +72,7 @@ $org = $user->getOrganization();
         <td width="50%">
             <table border="0" cellspacing="" cellpadding="4" width="100%">
                 <tr>
-                    <th width="150">Name:</th>
+                    <th width="150"><?php echo __('Name'); ?>:</th>
                     <td><b><a href="#users/<?php echo $user->getId();
                     ?>/edit" class="user-action"><i
                     class="icon-edit"></i>&nbsp;<?php echo
@@ -73,13 +80,13 @@ $org = $user->getOrganization();
                     ?></a></td>
                 </tr>
                 <tr>
-                    <th>Email:</th>
+                    <th><?php echo __('Email'); ?>:</th>
                     <td>
                         <span id="user-<?php echo $user->getId(); ?>-email"><?php echo $user->getEmail(); ?></span>
                     </td>
                 </tr>
                 <tr>
-                    <th>Organization:</th>
+                    <th><?php echo __('Organization'); ?>:</th>
                     <td>
                         <span id="user-<?php echo $user->getId(); ?>-org">
                         <?php
@@ -99,16 +106,16 @@ $org = $user->getOrganization();
         <td width="50%" style="vertical-align:top">
             <table border="0" cellspacing="" cellpadding="4" width="100%">
                 <tr>
-                    <th width="150">Status:</th>
+                    <th width="150"><?php echo __('Status'); ?>:</th>
                     <td> <span id="user-<?php echo $user->getId();
                     ?>-status"><?php echo $user->getAccountStatus(); ?></span></td>
                 </tr>
                 <tr>
-                    <th>Created:</th>
+                    <th><?php echo __('Created'); ?>:</th>
                     <td><?php echo Format::db_datetime($user->getCreateDate()); ?></td>
                 </tr>
                 <tr>
-                    <th>Updated:</th>
+                    <th><?php echo __('Updated'); ?>:</th>
                     <td><?php echo Format::db_datetime($user->getUpdateDate()); ?></td>
                 </tr>
             </table>
@@ -119,9 +126,9 @@ $org = $user->getOrganization();
 <div class="clear"></div>
 <ul class="tabs">
     <li><a class="active" id="tickets_tab" href="#tickets"><i
-    class="icon-list-alt"></i>&nbsp;User Tickets</a></li>
+    class="icon-list-alt"></i>&nbsp;<?php echo __('User Tickets'); ?></a></li>
     <li><a id="notes_tab" href="#notes"><i
-    class="icon-pushpin"></i>&nbsp;Notes</a></li>
+    class="icon-pushpin"></i>&nbsp;<?php echo __('Notes'); ?></a></li>
 </ul>
 <div id="tickets" class="tab_content">
 <?php
@@ -138,20 +145,25 @@ include STAFFINC_DIR . 'templates/notes.tmpl.php';
 </div>
 
 <div style="display:none;" class="dialog" id="confirm-action">
-    <h3>Please Confirm</h3>
+    <h3><?php echo __('Please Confirm'); ?></h3>
     <a class="close" href=""><i class="icon-remove-circle"></i></a>
     <hr/>
     <p class="confirm-action" style="display:none;" id="banemail-confirm">
-        Are you sure want to <b>ban</b> <?php echo $user->getEmail(); ?>? <br><br>
-        New tickets from the email address will be auto-rejected.
+        <?php echo sprintf(__('Are you sure want to <b>ban</b> %s?'), $user->getEmail()); ?>
+        <br><br>
+        <?php echo __('New tickets from the email address will be auto-rejected.'); ?>
     </p>
     <p class="confirm-action" style="display:none;" id="confirmlink-confirm">
-        Are you sure want to send <b>Account Activation Link</b> to <em><?php echo $user->getEmail()?></em>?
+        <?php echo sprintf(__(
+        'Are you sure want to send an <b>Account Activation Link</b> to <em> %s </em>?'),
+        $user->getEmail()); ?>
     </p>
     <p class="confirm-action" style="display:none;" id="pwreset-confirm">
-        Are you sure want to send <b>Password Reset Link</b> to <em><?php echo $user->getEmail()?></em>?
+        <?php echo sprintf(__(
+        'Are you sure want to send a <b>Password Reset Link</b> to <em> %s </em>?'),
+        $user->getEmail()); ?>
     </p>
-    <div>Please confirm to continue.</div>
+    <div><?php echo __('Please confirm to continue.'); ?></div>
     <form action="users.php?id=<?php echo $user->getId(); ?>" method="post" id="confirm-form" name="confirm-form">
         <?php csrf_token(); ?>
         <input type="hidden" name="id" value="<?php echo $user->getId(); ?>">
@@ -159,11 +171,11 @@ include STAFFINC_DIR . 'templates/notes.tmpl.php';
         <input type="hidden" name="do" id="action" value="">
         <hr style="margin-top:1em"/>
         <p class="full-width">
-            <span class="buttons" style="float:left">
-                <input type="button" value="Cancel" class="close">
+            <span class="buttons pull-left">
+                <input type="button" value="<?php echo __('Cancel'); ?>" class="close">
             </span>
-            <span class="buttons" style="float:right">
-                <input type="submit" value="OK">
+            <span class="buttons pull-right">
+                <input type="submit" value="<?php echo __('OK'); ?>">
             </span>
          </p>
     </form>
diff --git a/include/staff/users.inc.php b/include/staff/users.inc.php
index 222cceb373b63686112e268ad3070ffed5ec74ce..ec35f2205af0052a6cf6eab62716870e59f82a06 100644
--- a/include/staff/users.inc.php
+++ b/include/staff/users.inc.php
@@ -34,6 +34,7 @@ if ($_REQUEST['query']) {
 
 $sortOptions = array('name' => 'user.name',
                      'email' => 'email.address',
+                     'status' => 'account_status',
                      'create' => 'user.created',
                      'update' => 'user.updated');
 $orderWays = array('DESC'=>'DESC','ASC'=>'ASC');
@@ -73,8 +74,8 @@ $qhash = md5($query);
 $_SESSION['users_qs_'.$qhash] = $query;
 
 ?>
-<h2>User Directory</h2>
-<div style="width:700px; float:left;">
+<h2><?php echo __('User Directory'); ?></h2>
+<div class="pull-left" style="width:700px;">
     <form action="users.php" method="get">
         <?php csrf_token(); ?>
         <input type="hidden" name="a" value="search">
@@ -82,25 +83,26 @@ $_SESSION['users_qs_'.$qhash] = $query;
             <tr>
                 <td><input type="text" id="basic-user-search" name="query" size=30 value="<?php echo Format::htmlchars($_REQUEST['query']); ?>"
                 autocomplete="off" autocorrect="off" autocapitalize="off"></td>
-                <td><input type="submit" name="basic_search" class="button" value="Search"></td>
+                <td><input type="submit" name="basic_search" class="button" value="<?php echo __('Search'); ?>"></td>
                 <!-- <td>&nbsp;&nbsp;<a href="" id="advanced-user-search">[advanced]</a></td> -->
             </tr>
         </table>
     </form>
  </div>
- <div style="float:right;text-align:right;padding-right:5px;">
-    <b><a href="#users/add" class="Icon newstaff popup-dialog">Add User</a></b>
+ <div class="pull-right flush-right" style="padding-right:5px;">
+    <b><a href="#users/add" class="Icon newstaff popup-dialog"><?php echo __('Add User'); ?></a></b>
     |
-    <b><a href="#users/import" class="popup-dialog"><i class="icon-cloud-upload icon-large"></i> Import</a></b>
+    <b><a href="#users/import" class="popup-dialog"><i class="icon-cloud-upload icon-large"></i>
+    <?php echo __('Import'); ?></a></b>
 </div>
 <div class="clear"></div>
 <?php
-$showing = $search ? 'Search Results: ' : '';
+$showing = $search ? __('Search Results').': ' : '';
 $res = db_query($query);
 if($res && ($num=db_num_rows($res)))
     $showing .= $pageNav->showing();
 else
-    $showing .= 'No users found!';
+    $showing .= __('No users found!');
 ?>
 <form action="users.php" method="POST" name="staff" >
  <?php csrf_token(); ?>
@@ -110,10 +112,14 @@ else
     <caption><?php echo $showing; ?></caption>
     <thead>
         <tr>
-            <th width="350"><a <?php echo $name_sort; ?> href="users.php?<?php echo $qstr; ?>&sort=name">Name</a></th>
-            <th width="250"><a  <?php echo $status_sort; ?> href="users.php?<?php echo $qstr; ?>&sort=status">Status</a></th>
-            <th width="100"><a <?php echo $create_sort; ?> href="users.php?<?php echo $qstr; ?>&sort=create">Created</a></th>
-            <th width="145"><a <?php echo $update_sort; ?> href="users.php?<?php echo $qstr; ?>&sort=update">Updated</a></th>
+            <th width="350"><a <?php echo $name_sort; ?> href="users.php?<?php
+                echo $qstr; ?>&sort=name"><?php echo __('Name'); ?></a></th>
+            <th width="250"><a  <?php echo $status_sort; ?> href="users.php?<?php
+                echo $qstr; ?>&sort=status"><?php echo __('Status'); ?></a></th>
+            <th width="100"><a <?php echo $create_sort; ?> href="users.php?<?php
+                echo $qstr; ?>&sort=create"><?php echo __('Created'); ?></a></th>
+            <th width="145"><a <?php echo $update_sort; ?> href="users.php?<?php
+                echo $qstr; ?>&sort=update"><?php echo __('Updated'); ?></a></th>
         </tr>
     </thead>
     <tbody>
@@ -131,7 +137,7 @@ else
                 if ($row['account_id'])
                     $status = new UserAccountStatus($row['account_status']);
                 else
-                    $status = 'Guest';
+                    $status = __('Guest');
 
                 $sel=false;
                 if($ids && in_array($row['id'], $ids))
@@ -159,8 +165,8 @@ else
 </table>
 <?php
 if($res && $num): //Show options..
-    echo sprintf('<div>&nbsp;Page: %s &nbsp; <a class="no-pjax"
-            href="users.php?a=export&qh=%s">Export</a></div>',
+    echo sprintf('<div>&nbsp;'.__('Page').': %s &nbsp; <a class="no-pjax"
+            href="users.php?a=export&qh=%s">'.__('Export').'</a></div>',
             $pageNav->getPageLinks(),
             $qhash);
 endif;
diff --git a/include/upgrader/aborted.inc.php b/include/upgrader/aborted.inc.php
index 8b8d8cbdb387c36d51581e58c77096e726f30bd1..5e4b1e9f74e8ef35fa66a0412214aec07537e6f7 100644
--- a/include/upgrader/aborted.inc.php
+++ b/include/upgrader/aborted.inc.php
@@ -1,12 +1,12 @@
 <?php
 if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied');
-?>    
+?>
 <div id="upgrader">
    <div id="main">
-    <h1 style="color:#FF7700;">Upgrade Aborted!</h1>
+    <h1 style="color:#FF7700;"><?php echo __('Upgrade Aborted!');?></h1>
     <div id="intro">
-        <p><strong>Upgrade aborted due to errors. Any errors at this stage are fatal.</strong></p>
-        <p>Please note the error(s), if any, when <a target="_blank" href="http://osticket.com/support/">seeking help</a>.<p>
+        <p><strong><?php echo __('Upgrade aborted due to errors. Any errors at this stage are fatal.');?></strong></p>
+        <p><?php echo sprintf(__('Please note the error(s), if any, when %1$s seeking help %2$s.'),'<a target="_blank" href="http://osticket.com/support/">','</a>');?><p>
         <?php
         if($upgrader && ($errors=$upgrader->getErrors())) {
             if($errors['err'])
@@ -17,18 +17,19 @@ if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access D
                 echo sprintf('<li>%s</li>',$error);
             echo '</ul>';
         } else {
-            echo '<b><font color="red">Internal error occurred - get technical help.</font></b>';
+            echo '<b><font color="red">'.__('Internal error occurred - get technical help.').'</font></b>';
         }
         ?>
-        <p><b>For more detailed information, please view <a href="logs.php">system logs</a> or check your email.</b></p>
+        <p><b><?php echo sprintf(__('For details - please view %s or check your email.'),
+            sprintf('<a href="logs.php">%s</a>', __('System Logs')));?></b></p>
         <br>
-        <p>Please, refer to the <a target="_blank" href="http://osticket.com/wiki/Upgrade_and_Migration">Upgrade Guide</a> on the wiki for more information.</p>
+        <p><?php echo sprintf(__('Please refer to the %1$s Upgrade Guide %2$s for more information.'), '<a target="_blank" href="http://osticket.com/wiki/Upgrade_and_Migration">', '</a>');?></p>
     </div>
-    <p><strong>Need Help?</strong> We provide <a target="_blank" href="http://osticket.com/support/professional_services.php"><u>professional upgrade services</u></a> and commercial support. <a target="_blank" href="http://osticket.com/support/">Contact us</a> today for <u>expedited</u> help.</p>
-  </div>    
+    <p><strong><?php echo __('Need Help?');?></strong> <?php echo sprintf(__('We provide %1$s professional upgrade services %2$s and commercial support.'), '<a target="_blank" href="http://osticket.com/support/professional_services.php"><u>','</u></a>'); echo sprintf(__('%1$s Contact us %2$s today for <u>expedited</u> help.'), '<a target="_blank" href="http://osticket.com/support/">','</a>');?></p>
+  </div>
   <div id="sidebar">
-    <h3>What to do?</h3>
-    <p>Restore your previous version from backup and try again or <a target="_blank" href="http://osticket.com/support/">seek help</a>.</p>
+    <h3><?php echo __('What to do?');?></h3>
+    <p><?php echo sprintf(__('Restore your previous version from backup and try again or %1$s seek help %2$s.'), '<a target="_blank" href="http://osticket.com/support/">','</a>');?></p>
   </div>
   <div class="clear"></div>
 </div>
diff --git a/include/upgrader/done.inc.php b/include/upgrader/done.inc.php
index 1c01fb7cba5b353d8b3dc92385f063aa8903da88..4542b6f4c9804a82d7b573a7b3285376f90f41f2 100644
--- a/include/upgrader/done.inc.php
+++ b/include/upgrader/done.inc.php
@@ -1,29 +1,35 @@
 <?php
 if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied');
-//Destroy the upgrader - we're done! 
+//Destroy the upgrader - we're done!
 $_SESSION['ost_upgrader']=null;
-?> 
+?>
 <div id="upgrader">
     <div id="main">
-        <h1 style="color:green;">Upgrade Completed!</h1>
+        <h1 style="color:green;"><?php echo __('Upgrade Completed!');?></h1>
         <div id="intro">
-        <p>Congratulations! osTicket upgrade has completed successfully.</p>
-        <p>Please refer to <a href="http://osticket.com/wiki/Release_Notes" target="_blank">Release Notes</a> for more information about changes and/or new features.</p>
+        <p><?php echo __('Congratulations! osTicket upgrade has been completed successfully.');?></p>
+        <p><?php echo sprintf(__('Please refer to %s for more information about changes and/or new features.'),
+            sprintf('<a href="http://osticket.com/wiki/Release_Notes" target="_blank">%s</a>',
+            __('Release Notes')
+        ));?></p>
         </div>
-        <p>Once again, thank you for choosing osTicket.</p>
-        <p>Please feel free to <a target="_blank" href="http://osticket.com/support/">let us know</a> of any other improvements and features you would like to see in osTicket, so that we may add them in the future as we continue to develop better and better versions of osTicket.</p>
-        <p>We take user feedback seriously and we're dedicated to making changes based on your input.</p>
-        <p>Good luck.<p>
-        <p>osTicket Team.</p>
+        <p><?php echo __('Once again, thank you for choosing osTicket.');?></p>
+        <p><?php echo sprintf(__('Please feel free to %1$s let us know %2$s of any other improvements and features you would like to see in osTicket, so that we may add them in the future as we continue to develop better and better versions of osTicket.'), '<a target="_blank" href="http://osticket.com/support/">', '</a>');?></p>
+        <p><?php echo __("We take user feedback seriously and we're dedicated to making changes based on your input.");?></p>
+        <p><?php echo __('Good luck.');?><p>
+        <p><?php echo __('osTicket Team.');?></p>
         <br>
-        <p><b>PS</b>: Don't just make customers happy, make happy customers!</p>
+        <p><b><?php echo __('PS');?></b>: <?php echo __("Don't just make customers happy, make happy customers!");?></p>
     </div>
     <div id="sidebar">
-            <h3>What's Next?</h3>
-            <p><b>Post-upgrade</b>: You can now go to <a href="scp/settings.php" target="_blank">Admin Panel</a> to enable the system and explore the new features. For complete and up-to-date release notes, see <a href="http://osticket.com/wiki/Release_Notes" target="_blank">osTicket wiki</a></p>
-            <p><b>Stay up to date</b>: It's important to keep your osTicket installation up to date. Get announcements, security updates and alerts delivered directly to you! 
-            <a target="_blank" href="http://osticket.com/support/subscribe.php">Get in the loop</a> today and stay informed!</p>
-            <p><b>Commercial support available</b>: Get guidance and hands-on expertise to address unique challenges and make sure your osTicket runs smoothly, efficiently, and securely. <a target="_blank" href="http://osticket.com/support/commercial_support.php.php">Learn More!</a></p>
+            <h3><?php echo __("What's Next?");?></h3>
+            <p><b><?php echo __('Post-upgrade');?></b>: <?php
+            echo sprintf(__('You can now go to %s to enable the system and explore the new features. For complete and up-to-date release notes see the %s'),
+                sprintf('<a href="scp/settings.php" target="_blank">%s</a>', __('Admin Panel')),
+                sprintf('<a href="http://osticket.com/wiki/Release_Notes" target="_blank">%s</a>', __('osTicket Wiki')));?></p>
+            <p><b><?php echo __('Stay up to date');?></b>: <?php echo __("It's important to keep your osTicket installation up to date. Get announcements, security updates and alerts delivered directly to you!");?>
+            <?php echo sprintf(__('%1$s Get in the loop %2$s today and stay informed!'), '<a target="_blank" href="http://osticket.com/subscribe.php">', '</a>');?></p>
+            <p><b><?php echo __('Commercial support available');?></b>: <?php echo sprintf(__('Get guidance and hands-on expertise to address unique challenges and make sure your osTicket runs smoothly, efficiently, and securely. %1$s Learn More! %2$s'), '<a target="_blank" href="http://osticket.com/support">','</a>');?></p>
    </div>
    <div class="clear"></div>
 </div>
diff --git a/include/upgrader/prereq.inc.php b/include/upgrader/prereq.inc.php
index 98665cbc9dece47058ff2526164cb2cf4634fa3b..8cdd98e97e87ed89ac94d4a4ecdfa78bc4c30375 100644
--- a/include/upgrader/prereq.inc.php
+++ b/include/upgrader/prereq.inc.php
@@ -1,54 +1,55 @@
 <?php
 if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied');
 ?>
-<h2>osTicket Upgrader</h2>
+<h2><?php echo __('osTicket Upgrader');?></h2>
 <div id="upgrader">
-    
+
     <div id="main">
             <div id="intro">
-             <p>Thank you for being a loyal osTicket user!</p>
-             <p>The upgrade wizard will guide you every step of the way through the upgrade process. While we try to ensure that the upgrade process is straightforward and painless, we can't guarantee this will be the case for every user.</p>
+             <p><?php echo __('Thank you for being a loyal osTicket user!');?></p>
+             <p><?php echo __("The upgrade wizard will guide you every step of the way in the upgrade process. While we try to ensure that the upgrade process is straightforward and painless, we can't guarantee it will be the case for every user.");?></p>
             </div>
-            <h2>Getting ready!</h2>
-            <p>Before we begin, we'll check your server configuration to make sure you meet the minimum requirements to run the latest version of osTicket.</p>
-            <h3>Prerequisites: <font color="red"><?php echo $errors['prereq']; ?></font></h3>
-            These items are necessary in order to run the latest version of osTicket.
+            <h2><?php echo __('Getting ready!');?></h2>
+            <p><?php echo __("Before we begin, we'll check your server configuration to make sure you meet the minimum requirements to run the latest version of osTicket.");?></p>
+            <h3><?php echo __('Prerequisites');?>: <font color="red"><?php echo $errors['prereq']; ?></font></h3>
+            <?php echo __('These items are necessary in order to run the latest version of osTicket.');?>
             <ul class="progress">
                 <li class="<?php echo $upgrader->check_php()?'yes':'no'; ?>">
-                PHP v5.3 or greater - (<small><b><?php echo PHP_VERSION; ?></b></small>)</li>
+                <?php echo sprintf(__('%s or later'), 'PHP v5.3'); ?> - (<small><b><?php echo PHP_VERSION; ?></b></small>)</li>
                 <li class="<?php echo $upgrader->check_mysql()?'yes':'no'; ?>">
-                MySQLi extension for PHP - (<small><b><?php echo extension_loaded('mysqli')?'module loaded':'missing!'; ?></b></small>)</li>
+                <?php echo __('MySQLi extension for PHP'); ?>- (<small><b><?php
+                    echo extension_loaded('mysqli')?__('module loaded'):__('missing!'); ?></b></small>)</li>
                 <li class="<?php echo $upgrader->check_mysql_version()?'yes':'no'; ?>">
-                MySQL v5.0 or greater - (<small><b><?php echo db_version(); ?></b></small>)</li>
+                <?php echo sprintf(__('%s or later'), 'MySQL v5.0'); ?> - (<small><b><?php echo db_version(); ?></b></small>)</li>
             </ul>
-            <h3>Highly Recommended:</h3>
-            We highly recommend that you follow the steps below.
+            <h3><?php echo __('Higly Recommended');?>:</h3>
+            <?php echo __('We highly recommend that you follow the steps below.');?>
             <ul>
-                <li>Back up the current database if you haven't done so already.</li>
-                <li>Be patient. The upgrade process will take a couple of seconds.</li>
+                <li><?php echo __("Back up the current database if you haven't done so already."); ?></li>
+                <li><?php echo __("Be patient. The upgrade process will take a couple of seconds."); ?></li>
             </ul>
             <div id="bar">
                 <form method="post" action="upgrade.php" id="prereq">
                     <?php csrf_token(); ?>
                     <input type="hidden" name="s" value="prereq">
-                    <input class="btn"  type="submit" name="submit" value="Start Upgrade Now &raquo;">
+                    <input class="btn"  type="submit" name="submit" value="<?php echo __('Start Upgrade Now');?> &raquo;">
                 </form>
             </div>
     </div>
     <div id="sidebar">
-            <h3>Upgrade Tips</h3>
-            <p>1. Remember to back up your osTicket database</p>
-            <p>2. Refer to the <a href="http://osticket.com/wiki/Upgrade_and_Migration" target="_blank">Upgrade Guide</a> for the latest tips</a>
-            <p>3. If you experience any problems, you can always restore your files/dabase backup.</p>
-            <p>4. We can help! Feel free to <a href="http://osticket.com/support/" target="_blank">contact us </a> for professional help.</p>
+            <h3><?php echo __('Upgrade Tips');?></h3>
+            <p>1. <?php echo __('Remember to back up your osTicket database');?></p>
+            <p>2. <?php echo sprintf(__('Refer to %1$s Upgrade Guide %2$s for the latest tips'), '<a href="http://osticket.com/wiki/Upgrade_and_Migration" target="_blank">', '</a>');?></p>
+            <p>3. <?php echo __('If you experience any problems, you can always restore your files/database backup.');?></p>
+            <p>4. <?php echo sprintf(__('We can help, feel free to %1$s contact us %2$s for professional help.'), '<a href="http://osticket.com/support/" target="_blank">', '</a>');?></p>
 
     </div>
     <div class="clear"></div>
 </div>
-    
+
 <div id="overlay"></div>
 <div id="loading">
-    <h4>Doing stuff!</h4>
-    Please wait... while we upgrade your osTicket installation!
+    <h4><?php echo __('Doing stuff!');?></h4>
+    <?php echo __('Please wait... while we upgrade your osTicket installation!');?>
     <div id="msg"></div>
 </div>
diff --git a/include/upgrader/rename.inc.php b/include/upgrader/rename.inc.php
index b8fca94bc42351aad885c7fa77733f6f05210df6..408c2f75a06a066f39c3f1a8c7ed93b525557ed9 100644
--- a/include/upgrader/rename.inc.php
+++ b/include/upgrader/rename.inc.php
@@ -3,31 +3,31 @@ if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access D
 ?>
 <div id="upgrader">
     <br>
-    <h2 style="color:#FF7700;">Configuration file rename required!</h2>
+    <h2 style="color:#FF7700;"><?php echo __('Configuration file rename required!');?></h2>
     <div id="main">
             <div id="intro">
-             <p>To avoid possible conflicts, please take a minute to rename configuration file as shown below.</p>
+             <p><?php echo __('To avoid possible conflicts, please take a minute to rename configuration file as shown below.');?></p>
             </div>
-            <h3>Solution:</h3>
-            Rename file <b>include/settings.php</b> to <b>include/ost-config.php</b> and click continue below.
+            <h3><?php echo __('Solution');?>:</h3>
+            <?php echo __('Rename file <b>include/settings.php</b> to <b>include/ost-config.php</b> and click continue below.');?>
             <ul>
-                <li><b>CLI:</b><br><i>mv include/settings.php include/ost-config.php</i></li>
-                <li><b>FTP:</b><br> </li>
-                <li><b>Cpanel:</b><br> </li>
+                <li><b><?php echo __('CLI');?>:</b><br><i>mv include/settings.php include/ost-config.php</i></li>
+                <li><b><?php echo __('FTP');?>:</b><br> </li>
+                <li><b><?php echo __('Cpanel');?>:</b><br> </li>
             </ul>
-            <p>Please refer to the <a target="_blank" href="http://osticket.com/wiki/Upgrade_and_Migration">Upgrade Guide</a> for more information.</p>
+            <p><?php echo sprintf(__('Please refer to the %1$s Upgrade Guide %2$s for more information.'), '<a target="_blank" href="http://osticket.com/wiki/Upgrade_and_Migration">', '</a>');?></p>
             <div id="bar">
                 <form method="post" action="upgrade.php">
                     <?php csrf_token(); ?>
                     <input type="hidden" name="s" value="prereq">
-                    <input class="btn" type="submit" name="submit" value="Continue &raquo;">
+                    <input class="btn" type="submit" name="submit" value="<?php echo __('Continue');?> &raquo;">
                 </form>
             </div>
     </div>
     <div id="sidebar">
-            <h3>Need Help?</h3>
+            <h3><?php echo __('Need Help?');?></h3>
             <p>
-            If you are looking for a greater level of support, we provide <u>professional upgrade</u> and commercial support with guaranteed response times and access to the core development team. We can also help customize osTicket or even add new features to the system to meet your unique needs. <a target="_blank" href="http://osticket.com/support/professional_services.php">Learn More!</a>
+            <?php echo __('If you are looking for a greater level of support, we provide <u>professional upgrade</u> and commercial support with guaranteed response times and access to the core development team. We can also help customize osTicket or even add new features to the system to meet your unique needs. <a target="_blank" href="http://osticket.com/support">Learn More!</a>'); ?>
             </p>
     </div>
     <div class="clear"></div>
diff --git a/include/upgrader/streams/core.sig b/include/upgrader/streams/core.sig
index 324095cf42b6af08769dcb485913dacba7cc301d..3c66643467933d668c14629d21d672cb2db1eaf3 100644
--- a/include/upgrader/streams/core.sig
+++ b/include/upgrader/streams/core.sig
@@ -1 +1 @@
-8f99b8bf9bee63c8e4dc274ffbdda383
+b26f29a6bb5dbb3510b057632182d138
diff --git a/include/upgrader/streams/core/03ff59bf-b26f29a6.cleanup.sql b/include/upgrader/streams/core/03ff59bf-b26f29a6.cleanup.sql
new file mode 100644
index 0000000000000000000000000000000000000000..045605d3a0d6003adf1dcf425098ea417764b5fa
--- /dev/null
+++ b/include/upgrader/streams/core/03ff59bf-b26f29a6.cleanup.sql
@@ -0,0 +1,10 @@
+DELETE FROM  `%TABLE_PREFIX%config`
+    WHERE  `key` = 'properties' AND  `namespace` LIKE  'TS.%';
+
+DELETE FROM `%TABLE_PREFIX%ticket_status`
+    WHERE `state` = 'resolved';
+
+ALTER TABLE  `%TABLE_PREFIX%ticket_status`
+    DROP  `notes`;
+
+OPTIMIZE TABLE `%TABLE_PREFIX%ticket_status`;
diff --git a/include/upgrader/streams/core/03ff59bf-b26f29a6.patch.sql b/include/upgrader/streams/core/03ff59bf-b26f29a6.patch.sql
new file mode 100644
index 0000000000000000000000000000000000000000..ca097c81066c4ca07bc3706868fa4c3d2378f9ec
--- /dev/null
+++ b/include/upgrader/streams/core/03ff59bf-b26f29a6.patch.sql
@@ -0,0 +1,56 @@
+/**
+ * @version v1.9.4
+ * @signature b26f29a6bb5dbb3510b057632182d138
+ * @title Add properties filed and drop 'resolved' state
+ *
+ * This patch drops resolved state and any associated statuses
+ *
+ */
+
+-- Move tickets in resolved state to the default closed status
+SET @statusId = (
+        SELECT id FROM  `%TABLE_PREFIX%ticket_status`
+        WHERE  `state` =  'closed' ORDER BY id ASC LIMIT 1);
+
+UPDATE  `%TABLE_PREFIX%ticket` t1
+    JOIN `%TABLE_PREFIX%ticket_status` t2
+        ON (t2.id = t1.status_id)
+    SET t1.status_id = @statusId
+    WHERE t2.state='resolved';
+
+-- add properties field IF it doesn't exist
+SET @s = (SELECT IF(
+    (SELECT COUNT(*)
+        FROM INFORMATION_SCHEMA.COLUMNS
+        WHERE table_name = '%TABLE_PREFIX%ticket_status'
+        AND table_schema = DATABASE()
+        AND column_name = 'properties'
+    ) > 0,
+    "SELECT 1",
+    "ALTER TABLE `%TABLE_PREFIX%ticket_status` ADD `properties` text NOT NULL AFTER `sort`"
+));
+PREPARE stmt FROM @s;
+EXECUTE stmt;
+
+UPDATE `%TABLE_PREFIX%ticket_status` s
+    INNER JOIN `%TABLE_PREFIX%config` c
+        ON(c.namespace = CONCAT('TS.', s.id) AND c.key='properties')
+    SET s.properties = c.value;
+
+--  add default reopen settings to existing closed state statuses
+UPDATE `%TABLE_PREFIX%ticket_status`
+    SET `properties`= INSERT(`properties`, 2, 0, '"allowreopen":true,"reopenstatus":0,')
+    WHERE `state` = 'closed';
+
+-- change thread body text to 16Mb.
+ALTER TABLE  `%TABLE_PREFIX%ticket_thread`
+    CHANGE  `body`  `body` mediumtext NOT NULL;
+
+-- index ext id
+ALTER TABLE  `%TABLE_PREFIX%note`
+    ADD INDEX (`ext_id`);
+
+-- Set new schema signature
+UPDATE `%TABLE_PREFIX%config`
+    SET `value` = 'b26f29a6bb5dbb3510b057632182d138'
+    WHERE `key` = 'schema_signature' AND `namespace` = 'core';
diff --git a/include/upgrader/streams/core/8f99b8bf-03ff59bf.cleanup.sql b/include/upgrader/streams/core/8f99b8bf-03ff59bf.cleanup.sql
new file mode 100644
index 0000000000000000000000000000000000000000..dbeb1fa39788d22ebe82ba7ef6c559d2de363bcf
--- /dev/null
+++ b/include/upgrader/streams/core/8f99b8bf-03ff59bf.cleanup.sql
@@ -0,0 +1,10 @@
+DELETE FROM `%TABLE_PREFIX%config`
+    WHERE `namespace`='core' AND `key` = 'random_ticket_ids';
+
+ALTER TABLE `%TABLE_PREFIX%ticket`
+    DROP COLUMN `status`;
+
+-- Regenerate the CDATA table with the new format for 1.9.4
+DROP TABLE `%TABLE_PREFIX%ticket__cdata`;
+
+OPTIMIZE TABLE `%TABLE_PREFIX%ticket`;
diff --git a/include/upgrader/streams/core/8f99b8bf-03ff59bf.patch.sql b/include/upgrader/streams/core/8f99b8bf-03ff59bf.patch.sql
new file mode 100644
index 0000000000000000000000000000000000000000..a6c94d1598b5ba1fdb354cf8822296d6884c93f3
--- /dev/null
+++ b/include/upgrader/streams/core/8f99b8bf-03ff59bf.patch.sql
@@ -0,0 +1,82 @@
+/**
+ * @version v1.9.4
+ * @signature 03ff59bf35a58a102e9b32ad33c2839f
+ * @title Custom Ticket Numbers and Statuses
+ *
+ * This patch adds support for ticket number sequences to the database
+ * rather than the original implementation which had support for generating
+ * random numbers or using the database-created ticket_id value.
+ *
+ * This script will also migrate the previous settings, namely the
+ * use_random_ids config settings to the new system.
+ */
+
+DROP TABLE IF EXISTS `%TABLE_PREFIX%sequence`;
+CREATE TABLE `%TABLE_PREFIX%sequence` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `name` varchar(64) DEFAULT NULL,
+  `flags` int(10) unsigned DEFAULT NULL,
+  `next` bigint(20) unsigned NOT NULL DEFAULT '1',
+  `increment` int(11) DEFAULT '1',
+  `padding` char(1) DEFAULT '0',
+  `updated` datetime NOT NULL,
+  PRIMARY KEY (`id`)
+-- InnoDB is intended here because transaction support is required for row
+-- locking
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+SET @random = (SELECT `value` FROM `%TABLE_PREFIX%config`
+    WHERE `namespace` = 'core' AND `key` = 'random_ticket_ids');
+
+-- Sequence (id=1) will be loaded in the task file
+INSERT INTO `%TABLE_PREFIX%config` (`namespace`, `key`, `value`)
+    VALUES
+    ('core', 'number_format', IF(@random, '######', '#')),
+    ('core', 'sequence_id', IF(@random, 0, 1));
+
+ALTER TABLE `%TABLE_PREFIX%help_topic`
+    ADD `flags` int(10) unsigned DEFAULT '0' AFTER `noautoresp`,
+    ADD `sequence_id` int(10) unsigned NOT NULL DEFAULT '0' AFTER `form_id`,
+    ADD `number_format` varchar(32) DEFAULT NULL AFTER `topic`;
+
+ALTER TABLE  `%TABLE_PREFIX%list`
+    ADD  `masks` INT UNSIGNED NOT NULL DEFAULT  '0' AFTER  `sort_mode`,
+    ADD `type` VARCHAR( 16 ) NULL DEFAULT NULL AFTER `masks`,
+    ADD INDEX ( `type` );
+
+CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%ticket_status` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `name` varchar(60) NOT NULL DEFAULT '',
+  `state` varchar(16) DEFAULT NULL,
+  `mode` int(11) unsigned NOT NULL DEFAULT '0',
+  `flags` int(11) unsigned NOT NULL DEFAULT '0',
+  `sort` int(11) unsigned NOT NULL DEFAULT '0',
+  `properties` text NOT NULL,
+  `notes` text NOT NULL,
+  `created` datetime NOT NULL,
+  `updated` datetime NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `name` (`name`),
+  KEY `state` ( `state` )
+) DEFAULT CHARSET=utf8;
+
+ALTER TABLE  `%TABLE_PREFIX%help_topic`
+    ADD  `status_id` INT UNSIGNED NOT NULL DEFAULT  '0' AFTER  `noautoresp`;
+
+ALTER TABLE  `%TABLE_PREFIX%filter`
+    ADD  `status_id` INT UNSIGNED NOT NULL DEFAULT  '0' AFTER  `email_id`;
+
+ALTER TABLE  `%TABLE_PREFIX%ticket`
+    ADD  `status_id` INT UNSIGNED NOT NULL DEFAULT  '0' AFTER  `user_email_id`,
+    ADD INDEX (`status_id`);
+
+UPDATE `%TABLE_PREFIX%ticket` SET  `status_id` = 3
+    WHERE  `status` = 'closed';
+
+UPDATE `%TABLE_PREFIX%ticket` SET  `status_id` = 1
+        WHERE  `status` = 'open';
+
+-- Finished with patch
+UPDATE `%TABLE_PREFIX%config`
+    SET `value` = '03ff59bf35a58a102e9b32ad33c2839f'
+    WHERE `key` = 'schema_signature' AND `namespace` = 'core';
diff --git a/include/upgrader/streams/core/8f99b8bf-03ff59bf.task.php b/include/upgrader/streams/core/8f99b8bf-03ff59bf.task.php
new file mode 100644
index 0000000000000000000000000000000000000000..f7bb36f7db2da78056a307b730543f6cc43ad5b0
--- /dev/null
+++ b/include/upgrader/streams/core/8f99b8bf-03ff59bf.task.php
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * Loads the initial sequence from the inital data files
+ */
+
+class SequenceLoader extends MigrationTask {
+    var $description = "Loading initial data for sequences";
+
+    function run($max_time) {
+        global $cfg;
+
+        $i18n = new Internationalization($cfg->get('system_language', 'en_US'));
+        $sequences = $i18n->getTemplate('sequence.yaml')->getData();
+        foreach ($sequences as $s) {
+            Sequence::create($s)->save();
+        }
+        db_query('UPDATE '.SEQUENCE_TABLE.' SET `next`= '
+            .'(SELECT MAX(ticket_id)+1 FROM '.TICKET_TABLE.') '
+            .'WHERE `id`=1');
+
+        require_once(INCLUDE_DIR . 'class.list.php');
+
+        $lists = $i18n->getTemplate('list.yaml')->getData();
+        foreach ($lists as $l) {
+            DynamicList::create($l);
+        }
+
+        $statuses = $i18n->getTemplate('ticket_status.yaml')->getData();
+        foreach ($statuses as $s) {
+            TicketStatus::__create($s);
+        }
+
+        // Initialize MYSQL search backend
+        MysqlSearchBackend::__init();
+    }
+}
+
+return 'SequenceLoader';
+
+?>
diff --git a/include/upgrader/upgrade.inc.php b/include/upgrader/upgrade.inc.php
index 2a7bc3d98248b7a6d13230e9b1fd56eff23f0926..c6492c65cb311e404e9fd497f817e51136f13152 100644
--- a/include/upgrader/upgrade.inc.php
+++ b/include/upgrader/upgrade.inc.php
@@ -13,22 +13,18 @@ if(($mode = $ost->get_var('m', $_GET)) &&  $mode!=$upgrader->getMode()) {
 
 $action=$upgrader->getNextAction();
 ?>
-    <h2>Migrate to osTicket <?php echo THIS_VERSION; ?></h2>
+    <h2><?php echo sprintf(__('Migrate to osTicket %s'), THIS_VERSION); ?></h2>
 <div id="upgrader">
     <div id="main">
             <div id="intro">
-             <p>Thank you for taking the time to upgrade your osTicket intallation!</p>
-             <p><strong>Please don't cancel or close the browser; any errors
-             at this stage will be fatal.</strong></p>
+             <p><?php echo __('Thank you for taking the time to upgrade your osTicket intallation!');?></p>
+             <p><strong><?php echo __("Please don't cancel or close the browser. Any errors at this stage will be fatal.");?></strong></p>
             </div>
-            <h2 id="task">Applying updates to database stream:
-            <?php echo $upgrader->getCurrentStream()->name; ?></h2>
-            <p>In order to upgrade to this version of osTicket, a database
-            migration is required. This upgrader will automatically apply
-            the database patches shipped with osTicket since your last
-            upgrade.</p>
-            <p>The upgrade wizard will now attempt to upgrade your database and core settings!
-            Below is a summary of the database patches to be applied.
+            <h2 id="task"><?php echo sprintf(__('Applying updates to database stream: %s'),
+                $upgrader->getCurrentStream()->name); ?></h2>
+            <p><?php echo __('In order to upgrade to this version of osTicket, a database migration is required. This upgrader will automatically apply the database patches shipped with osTicket since your last upgrade.'); ?></p>
+            <p><?php echo __('The upgrade wizard will now attempt to upgrade your database and core settings!'); ?>
+            <?php echo __('Below is a summary of the database patches to be applied.'); ?>
             </p>
             <?php echo $upgrader->getUpgradeSummary(); ?>
             <div id="bar">
@@ -37,24 +33,24 @@ $action=$upgrader->getNextAction();
                     <input type="hidden" name="s" value="upgrade">
                     <input type="hidden" id="mode" name="m" value="<?php echo $upgrader->getMode(); ?>">
                     <input type="hidden" name="sh" value="<?php echo $upgrader->getSchemaSignature(); ?>">
-                    <input class="btn"  type="submit" name="submit" value="Upgrade Now!">
+                    <input class="btn"  type="submit" name="submit" value="<?php echo __('Upgrade Now!');?>">
                 </form>
             </div>
     </div>
     <div id="sidebar">
-            <h3>Upgrade Tips</h3>
-            <p>1. Be patient. The process will take a couple of minutes.</p>
-            <p>2. If you experience any problems, you can always restore your files/database backup.</p>
-            <p>3. We can help! Feel free to <a href="http://osticket.com/support/" target="_blank">contact us </a> for professional help.</p>
+            <h3><?php echo __('Upgrade Tips');?></h3>
+            <p>1. <?php echo __('Be patient the process will take a couple of minutes.');?></p>
+            <p>2. <?php echo __('If you experience any problems, you can always restore your files/database backup.');?></p>
+            <p>3. <?php echo sprintf(__('We can help. Feel free to %1$s contact us %2$s for professional help.'), '<a href="http://osticket.com/support" target="_blank">', '</a>');?></p>
     </div>
     <div class="clear"></div>
     <div id="upgrading">
         <i class="icon-spinner icon-spin icon-3x pull-left icon-light"></i>
         <div style="display: inline-block; width: 220px">
         <h4 id="action"><?php echo $action; ?></h4>
-        Please wait... while we upgrade your osTicket installation!
+        <?php echo __('Please wait... while we upgrade your osTicket installation!');?>
         <div id="msg" style="font-weight: bold;padding-top:10px;">
-            <?php echo sprintf("%s - Relax!", $thisstaff->getFirstName()); ?>
+            <?php echo sprintf(__('%s - Relax!'), $thisstaff->getFirstName()); ?>
         </div>
         </div>
     </div>
diff --git a/index.php b/index.php
index cb24a560687e7e72c75e0235a386e4b368036f67..2e526f6999014056b8fc491c0db74dfada677079 100644
--- a/index.php
+++ b/index.php
@@ -22,23 +22,29 @@ require(CLIENTINC_DIR.'header.inc.php');
     if($cfg && ($page = $cfg->getLandingPage()))
         echo $page->getBodyWithImages();
     else
-        echo  '<h1>Welcome to the Support Center</h1>';
+        echo  '<h1>'.__('Welcome to the Support Center').'</h1>';
     ?>
-    <div id="new_ticket">
-        <h3>Open A New Ticket</h3>
+    <div id="new_ticket" class="pull-left">
+        <h3><?php echo __('Open a New Ticket');?></h3>
         <br>
-        <div>Please provide as much detail as possible so we can best assist you. To update a previously submitted ticket, please login.</div>
-        <p>
-            <a href="open.php" class="green button">Open a New Ticket</a>
-        </p>
+        <div><?php echo __('Please provide as much detail as possible so we can best assist you. To update a previously submitted ticket, please login.');?></div>
     </div>
 
-    <div id="check_status">
-        <h3>Check Ticket Status</h3>
+    <div id="check_status" class="pull-right">
+        <h3><?php echo __('Check Ticket Status');?></h3>
         <br>
-        <div>We provide archives and history of all your current and past support requests complete with responses.</div>
+        <div><?php echo __('We provide archives and history of all your current and past support requests complete with responses.');?></div>
+    </div>
+
+    <div class="clear"></div>
+    <div class="front-page-button pull-left">
+        <p>
+            <a href="open.php" class="green button"><?php echo __('Open a New Ticket');?></a>
+        </p>
+    </div>
+    <div class="front-page-button pull-right">
         <p>
-            <a href="view.php" class="blue button">Check Ticket Status</a>
+            <a href="view.php" class="blue button"><?php echo __('Check Ticket Status');?></a>
         </p>
     </div>
 </div>
@@ -47,7 +53,11 @@ require(CLIENTINC_DIR.'header.inc.php');
 if($cfg && $cfg->isKnowledgebaseEnabled()){
     //FIXME: provide ability to feature or select random FAQs ??
 ?>
-<p>Be sure to browse our <a href="kb/index.php">Frequently Asked Questions (FAQs)</a>, before opening a ticket.</p>
+<p><?php echo sprintf(
+    __('Be sure to browse our %s before opening a ticket'),
+    sprintf('<a href="kb/index.php">%s</a>',
+        __('Frequently Asked Questions (FAQs)')
+    )); ?></p>
 </div>
 <?php
 } ?>
diff --git a/js/filedrop.field.js b/js/filedrop.field.js
new file mode 100644
index 0000000000000000000000000000000000000000..d07a2a675455cf3c5af0f8046a82aa5822c4497b
--- /dev/null
+++ b/js/filedrop.field.js
@@ -0,0 +1,832 @@
+!function($) {
+  "use strict";
+
+  var FileDropbox = function(element, options) {
+    this.$element = $(element);
+    this.uploads = [];
+
+    var events = {
+      uploadStarted: $.proxy(this.uploadStarted, this),
+      uploadFinished: $.proxy(this.uploadFinished, this),
+      progressUpdated: $.proxy(this.progressUpdated, this),
+      speedUpdated: $.proxy(this.speedUpdated, this),
+      dragOver: $.proxy(this.dragOver, this),
+      drop: $.proxy(this.drop, this),
+      beforeSend: $.proxy(this.beforeSend, this),
+      beforeEach: $.proxy(this.beforeEach, this),
+      error: $.proxy(this.handleError, this),
+      afterAll: $.proxy(this.afterAll, this)
+    };
+
+    this.options = $.extend({}, $.fn.filedropbox.defaults, events, options);
+    this.$element.filedrop(this.options);
+    if (this.options.shim) {
+      $('input[type=file]', this.$element).attr('name', this.options.name)
+          .addClass('shim').css('display', 'inline-block').show();
+      $('a.manual', this.$element).hide();
+    }
+    (this.options.files || []).forEach($.proxy(this.addNode, this));
+  };
+
+  FileDropbox.prototype = {
+    drop: function(e) {
+        this.$element.removeAttr('style');
+    },
+    dragOver: function(box, e) {
+        this.$element.css('background-color', 'rgba(0, 0, 0, 0.3)');
+    },
+    beforeEach: function (file) {
+      if (this.options.maxfiles && this.uploads.length >= this.options.maxfiles) {
+          // This file is not allowed to be added to the list. It's over the
+          // limit
+          this.handleError('TooManyFiles', file);
+          return false;
+      }
+      var node = this.addNode(file).data('file', file);
+      node.find('.progress').show();
+      node.find('.progress-bar').width('100%').addClass('progress-bar-striped active');
+      node.find('.trash').hide();
+    },
+    beforeSend: function (file, i, reader) {
+      var URL = window.webkitURL || window.URL;
+      this.uploads.some(function(e) {
+        if (e.data('file') == file) {
+          if (file.type.indexOf('image/') === 0 && file.size < 1e6) {
+            var img = e.find('.preview')
+              .tooltip({items:'img',
+                tooltipClass: 'tooltip-preview',
+                content:function(){ return $(this).clone().wrap('<div>'); }}
+              )
+              .get()[0];
+              img.src = URL.createObjectURL(file);
+          }
+          return true;
+        }
+      });
+    },
+    speedUpdated: function(i, file, speed) {
+      var that = this;
+      this.uploads.some(function(e) {
+        if (e.data('file') == file) {
+          e.find('.upload-rate').text(that.fileSize(speed * 1024)+'/s');
+          return true;
+        }
+      });
+    },
+    progressUpdated: function(i, file, value) {
+      this.uploads.some(function(e) {
+        if (e.data('file') == file) {
+          e.find('.progress-bar')
+            .attr({'aria-valuenow': value})
+            .width(value + '%');
+          if (value == 100)
+            e.find('.progress-bar').addClass('progress-bar-striped active');
+          return true;
+        }
+      });
+    },
+    uploadStarted: function(i, file, n, xhr) {
+      var that = this;
+      this.uploads.some(function(e) {
+        if (e.data('file') == file) {
+          e.data('xhr', xhr);
+          e.find('.cancel').show();
+          that.lockSubmit(1);
+          that.progressUpdated(i, file, 0);
+          setTimeout(function() {
+            e.find('.progress-bar')
+             .removeClass('progress-bar-striped active');
+          }, 50);
+          return true;
+        }
+      });
+    },
+    uploadFinished: function(i, file, json, time, xhr) {
+      var that = this;
+      this.uploads.some(function(e) {
+        if (e.data('file') == file) {
+          if (!json || !json.id)
+            // Upload failed. TODO: Add a button to the UI to retry on
+            // HTTP 500
+            return e.remove();
+          e.find('[name="'+that.options.name+'"]').val(json.id);
+          e.data('fileId', json.id);
+          e.find('.progress-bar')
+            .width('100%')
+            .attr({'aria-valuenow': 100});
+          e.find('.trash').show();
+          e.find('.upload-rate').hide();
+          e.find('.cancel').hide();
+          setTimeout(function() { e.find('.progress').hide(); }, 600);
+          return true;
+        }
+      });
+    },
+    fileSize: function(size) {
+      var sizes = ['k','M','G','T'],
+          suffix = '';
+      while (size > 900) {
+        size /= 1024;
+        suffix = sizes.shift();
+      }
+      return (suffix ? size.toPrecision(3) + suffix : size) + 'B';
+    },
+    findNode: function(file) {
+      var nodes = $.grep(this.uploads, function(e) {
+        return e.data('file') == file;
+      });
+      return nodes ? nodes[0] : null;
+    },
+    addNode: function(file) {
+      // Check if the file is already in the list of files for this dropbox
+      var already_added = false;
+      this.uploads.some(function(e) {
+        if (file.id && e.data('fileId') == file.id) {
+          already_added = true;
+          return true;
+        }
+      });
+      if (already_added)
+          return;
+
+      var filenode = $('<div class="file"></div>');
+      filenode
+          .append($('<div class="filetype"></div>').addClass())
+          .append($('<img class="preview" />'))
+          .append($('<span class="filename ltr"></div>')
+            .append($('<span class="filesize"></span>').text(
+              this.fileSize(parseInt(file.size))
+            ))
+            .append($('<div class="pull-right cancel"></div>')
+              .append($('<i class="icon-remove"></i>')
+                .attr('title', __('Cancel'))
+              )
+              .click($.proxy(this.cancelUpload, this, filenode))
+              .hide()
+            )
+            .append($('<div class="upload-rate pull-right"></div>'))
+          ).append($('<div class="progress"></div>')
+            .append($('<div class="progress-bar"></div>'))
+            .attr({'aria-valuemin':0,'aria-valuemax':100})
+            .hide())
+          .append($('<input type="hidden"/>').attr('name', this.options.name)
+            .val(file.id))
+          .append($('<div class="clear"></div>'));
+      if (this.options.deletable) {
+        filenode.prepend($('<span><i class="icon-trash"></i></span>')
+          .addClass('trash pull-right')
+          .click($.proxy(this.deleteNode, this, filenode))
+        );
+      }
+      if (file.id)
+        filenode.data('fileId', file.id);
+      if (file.download)
+        filenode.find('.filename').prepend(
+          $('<a class="no-pjax" target="_blank"></a>').text(file.name)
+            .attr('href', 'file.php?h='+escape(file.download))
+        );
+      else
+        filenode.find('.filename').prepend($('<span>').text(file.name));
+      this.$element.parent().find('.files').append(filenode);
+      this.uploads.push(filenode);
+      return filenode;
+    },
+    deleteNode: function(filenode, e) {
+      if (!e || confirm(__('You sure?'))) {
+        var i = this.uploads.indexOf(filenode);
+        if (i !== -1)
+            this.uploads.splice(i,1);
+        filenode.slideUp('fast', function() { this.remove(); });
+      }
+    },
+    cancelUpload: function(node) {
+      if (node.data('xhr')) {
+        node.data('xhr').abort();
+        var img = node.find('.preview').get()[0];
+        if (img) (window.webkitURL || window.URL).revokeObjectURL(img.src);
+      }
+      return this.deleteNode(node, false);
+    },
+    handleError: function(err, file, i, status) {
+      var message = $.fn.filedropbox.messages[err],
+          filenode = this.findNode(file);
+      if (file instanceof File) {
+        message = '<b>' + file.name + '</b><br/>' + message + '<br/>' + status;
+      }
+      $.sysAlert(__('File Upload Error'), message);
+      if (filenode) this.cancelUpload(filenode);
+    },
+    afterAll: function() {
+      var submit = this.$element.closest('form').find('input[type=submit]'),
+          $submit = $(submit);
+      if ($submit.data('original')) {
+        $submit.val($submit.data('original')).prop('disabled', false);
+      }
+    },
+    lockSubmit: function() {
+      var submit = this.$element.closest('form').find('input[type=submit]'),
+          $submit = $(submit);
+      if (!$submit.data('original')) {
+        $submit.data('original', $submit.val());
+      }
+      $submit.val(__('Uploading ...')).prop('disabled', true);
+    }
+  };
+
+  $.fn.filedropbox = function ( option ) {
+    return this.each(function () {
+      var $this = $(this),
+        data = $this.data('dropbox'),
+        options = typeof option == 'object' && option;
+      if (!data) $this.data('dropbox', (data = new FileDropbox(this, options)));
+      if (typeof option == 'string') data[option]();
+    });
+  };
+
+  $.fn.filedropbox.defaults = {
+    files: [],
+    deletable: true,
+    shim: !window.FileReader,
+    queuefiles: 4
+  };
+
+  $.fn.filedropbox.messages = {
+    'BrowserNotSupported': __('Your browser is not supported'),
+    'TooManyFiles': __('You are trying to upload too many files'),
+    'FileTooLarge': __('File is too large'),
+    'FileTypeNotAllowed': __('This type of file is not allowed'),
+    'FileExtensionNotAllowed': __('This type of file is not allowed'),
+    'NotFound': __('Could not find or read this file'),
+    'NotReadable': __('Could not find or read this file'),
+    'AbortError': __('Could not find or read this file')
+  };
+
+  $.fn.filedropbox.Constructor = FileDropbox;
+
+}(jQuery);
+
+/*
+ * Default text - jQuery plugin for html5 dragging files from desktop to browser
+ *
+ * Author: Weixi Yen
+ *
+ * Email: [Firstname][Lastname]@gmail.com
+ *
+ * Copyright (c) 2010 Resopollution
+ *
+ * Licensed under the MIT license:
+ *   http://www.opensource.org/licenses/mit-license.php
+ *
+ * Project home:
+ *   http://www.github.com/weixiyen/jquery-filedrop
+ *
+ * Version:  0.1.0
+ *
+ * Features:
+ *      Allows sending of extra parameters with file.
+ *      Works with Firefox 3.6+
+ *      Future-compliant with HTML5 spec (will work with Webkit browsers and IE9)
+ * Usage:
+ *  See README at project homepage
+ *
+ */
+;(function($) {
+
+  jQuery.event.props.push("dataTransfer");
+
+  var default_opts = {
+      fallback_id: '',
+      link: false,
+      url: '',
+      refresh: 1000,
+      paramname: 'userfile',
+      requestType: 'POST',    // just in case you want to use another HTTP verb
+      allowedfileextensions:[],
+      allowedfiletypes:[],
+      maxfiles: 25,           // Ignored if queuefiles is set > 0
+      maxfilesize: 1,         // MB file size limit
+      queuefiles: 0,          // Max files before queueing (for large volume uploads)
+      queuewait: 200,         // Queue wait time if full
+      data: {},
+      headers: {},
+      drop: empty,
+      dragStart: empty,
+      dragEnter: empty,
+      dragOver: empty,
+      dragLeave: empty,
+      docEnter: empty,
+      docOver: empty,
+      docLeave: empty,
+      beforeEach: empty,
+      afterAll: empty,
+      rename: empty,
+      error: function(err, file, i, status) {
+        alert(err);
+      },
+      uploadStarted: empty,
+      uploadFinished: empty,
+      progressUpdated: empty,
+      globalProgressUpdated: empty,
+      speedUpdated: empty
+      },
+      errors = ["BrowserNotSupported", "TooManyFiles", "FileTooLarge", "FileTypeNotAllowed", "NotFound", "NotReadable", "AbortError", "ReadError", "FileExtensionNotAllowed"];
+
+  $.fn.filedrop = function(options) {
+    var opts = $.extend({}, default_opts, options),
+        global_progress = [],
+        doc_leave_timer, stop_loop = false,
+        files_count = 0,
+        files;
+
+    if (window.FileReader)
+      $('#' + opts.fallback_id).css({
+        display: 'none',
+        width: 0,
+        height: 0
+      });
+
+    this.on('drop', drop).on('dragstart', opts.dragStart).on('dragenter', dragEnter).on('dragover', dragOver).on('dragleave', dragLeave);
+    $(document).on('drop', docDrop).on('dragenter', docEnter).on('dragover', docOver).on('dragleave', docLeave);
+
+    (opts.link || this).on('click', function(e){
+      $('#' + opts.fallback_id).trigger(e);
+      return false;
+    });
+
+    $('#' + opts.fallback_id).change(function(e) {
+      opts.drop(e);
+      files = e.target.files;
+      files_count = files.length;
+      upload();
+    });
+
+    function drop(e) {
+      if( opts.drop.call(this, e) === false ) return false;
+      if(!e.dataTransfer)
+        return;
+      files = e.dataTransfer.files;
+      if (files === null || files === undefined || files.length === 0) {
+        opts.error(errors[0]);
+        return false;
+      }
+      files_count = files.length;
+      upload();
+      e.preventDefault();
+      return false;
+    }
+
+    function getBuilder(filename, filedata, mime, boundary) {
+      var dashdash = '--',
+          crlf = '\r\n',
+          builder = [],
+          paramname = opts.paramname,
+          Blob = window.WebKitBlob || window.Blob;
+
+      if (opts.data) {
+        var params = $.param(opts.data).replace(/\+/g, '%20').split(/&/);
+
+        $.each(params, function() {
+          var pair = this.split("=", 2),
+              name = decodeURIComponent(pair[0]),
+              val  = decodeURIComponent(pair[1]);
+
+          if (pair.length !== 2) {
+              return;
+          }
+
+          builder.push(dashdash
+              + boundary
+              + crlf
+              + 'Content-Disposition: form-data; name="' + name + '"'
+              + crlf
+              + crlf
+              + val
+              + crlf);
+        });
+      }
+
+      if (jQuery.isFunction(paramname)){
+        paramname = paramname(filename);
+      }
+
+      builder.push(dashdash
+          + boundary
+          + crlf
+          + 'Content-Disposition: form-data; name="' + (paramname||"") + '"'
+          + '; filename="' + encodeURIComponent(filename) + '"'
+          + crlf
+
+          + 'Content-Type: ' + mime
+          + crlf
+          + crlf);
+
+      builder.push(filedata);
+      builder.push(crlf
+          + dashdash
+          + boundary
+          + dashdash
+          + crlf);
+      return new Blob(builder);
+    }
+
+    function progress(e) {
+      if (e.lengthComputable) {
+        var percentage = ((e.loaded * 100) / e.total).toFixed(1);
+        if (this.currentProgress != percentage) {
+
+          this.currentProgress = percentage;
+          opts.progressUpdated(this.index, this.file, this.currentProgress);
+
+          global_progress[this.global_progress_index] = this.currentProgress;
+          globalProgress();
+
+          var elapsed = new Date().getTime();
+          var diffTime = elapsed - this.currentStart;
+          if (diffTime >= opts.refresh) {
+            var diffData = e.loaded - this.startData;
+            var speed = diffData / diffTime; // KB per second
+            opts.speedUpdated(this.index, this.file, speed);
+            this.startData = e.loaded;
+            this.currentStart = elapsed;
+          }
+        }
+      }
+    }
+
+    function globalProgress() {
+      if (global_progress.length === 0) {
+        return;
+      }
+
+      var total = 0, index;
+      for (index in global_progress) {
+        if(global_progress.hasOwnProperty(index)) {
+          total = total + global_progress[index];
+        }
+      }
+
+      opts.globalProgressUpdated(Math.round(total / global_progress.length));
+    }
+
+    // Respond to an upload
+    function upload() {
+      stop_loop = false;
+
+      if (!files) {
+        opts.error(errors[0]);
+        return false;
+      }
+
+      if (opts.allowedfiletypes.push && opts.allowedfiletypes.length) {
+        for(var fileIndex = files.length;fileIndex--;) {
+          if(!files[fileIndex].type || $.inArray(files[fileIndex].type, opts.allowedfiletypes) < 0) {
+            opts.error(errors[3], files[fileIndex], fileIndex);
+            return false;
+          }
+        }
+      }
+
+      if (opts.allowedfileextensions.push && opts.allowedfileextensions.length) {
+        for(var fileIndex = files.length;fileIndex--;) {
+          var allowedextension = false;
+          for (i=0;i<opts.allowedfileextensions.length;i++){
+            if (files[fileIndex].name.substr(files[fileIndex].name.length-opts.allowedfileextensions[i].length).toLowerCase()
+                    == opts.allowedfileextensions[i].toLowerCase()
+            ) {
+              allowedextension = true;
+            }
+          }
+          if (!allowedextension){
+            opts.error(errors[8], files[fileIndex], fileIndex);
+            return false;
+          }
+        }
+      }
+
+      var filesDone = 0,
+          filesRejected = 0;
+
+      if (files_count > opts.maxfiles && opts.queuefiles === 0) {
+        opts.error(errors[1]);
+        return false;
+      }
+
+      // Define queues to manage upload process
+      var workQueue = [];
+      var processingQueue = [];
+      var doneQueue = [];
+
+      // Add everything to the workQueue
+      for (var i = 0; i < files_count; i++) {
+        workQueue.push(i);
+      }
+
+      // Helper function to enable pause of processing to wait
+      // for in process queue to complete
+      var pause = function(timeout) {
+        setTimeout(process, timeout);
+        return;
+      };
+
+      // Process an upload, recursive
+      var process = function() {
+
+        var fileIndex;
+
+        if (stop_loop) {
+          return false;
+        }
+
+        // Check to see if are in queue mode
+        if (opts.queuefiles > 0 && processingQueue.length >= opts.queuefiles) {
+          return pause(opts.queuewait);
+        } else {
+          // Take first thing off work queue
+          fileIndex = workQueue[0];
+          workQueue.splice(0, 1);
+
+          // Add to processing queue
+          processingQueue.push(fileIndex);
+        }
+
+        try {
+          if (beforeEach(files[fileIndex]) !== false) {
+            if (fileIndex === files_count) {
+              return;
+            }
+            var reader = new FileReader(),
+                max_file_size = 1048576 * opts.maxfilesize;
+
+            reader.index = fileIndex;
+            if (files[fileIndex].size > max_file_size) {
+              opts.error(errors[2], files[fileIndex], fileIndex);
+              // Remove from queue
+              processingQueue.forEach(function(value, key) {
+                if (value === fileIndex) {
+                  processingQueue.splice(key, 1);
+                }
+              });
+              filesRejected++;
+              return true;
+            }
+
+            reader.onerror = function(e) {
+                switch(e.target.error.code) {
+                    case e.target.error.NOT_FOUND_ERR:
+                        opts.error(errors[4], files[fileIndex], fileIndex);
+                        return false;
+                    case e.target.error.NOT_READABLE_ERR:
+                        opts.error(errors[5], files[fileIndex], fileIndex);
+                        return false;
+                    case e.target.error.ABORT_ERR:
+                        opts.error(errors[6], files[fileIndex], fileIndex);
+                        return false;
+                    default:
+                        opts.error(errors[7], files[fileIndex], fileIndex);
+                        return false;
+                };
+            };
+
+            reader.onloadend = function(e) {
+              if (!opts.beforeSend
+                  || false !== opts.beforeSend(files[fileIndex], fileIndex, e.target))
+                return send(e);
+            };
+
+            reader.readAsArrayBuffer(files[fileIndex]);
+
+          } else {
+            filesRejected++;
+          }
+        } catch (err) {
+          // Remove from queue
+          processingQueue.forEach(function(value, key) {
+            if (value === fileIndex) {
+              processingQueue.splice(key, 1);
+            }
+          });
+          opts.error(errors[0], files[fileIndex], fileIndex, err);
+          return false;
+        }
+
+        // If we still have work to do,
+        if (workQueue.length > 0) {
+          process();
+        }
+      };
+
+      var send = function(e) {
+
+        var fileIndex = (e.srcElement || e.target).index;
+
+        // Sometimes the index is not attached to the
+        // event object. Find it by size. Hack for sure.
+        if (e.target.index === undefined) {
+          e.target.index = getIndexBySize(e.total);
+        }
+
+        var xhr = new XMLHttpRequest(),
+            upload = xhr.upload,
+            file = files[e.target.index],
+            index = e.target.index,
+            start_time = new Date().getTime(),
+            boundary = '------multipartformboundary' + (new Date()).getTime(),
+            global_progress_index = global_progress.length,
+            builder,
+            newName = rename(file.name),
+            mime = file.type;
+
+        if (opts.withCredentials) {
+          xhr.withCredentials = opts.withCredentials;
+        }
+
+        var data = e.target.result;
+        if (typeof newName === "string") {
+          builder = getBuilder(newName, data, mime, boundary);
+        } else {
+          builder = getBuilder(file.name, data, mime, boundary);
+        }
+
+        upload.index = index;
+        upload.file = file;
+        upload.downloadStartTime = start_time;
+        upload.currentStart = start_time;
+        upload.currentProgress = 0;
+        upload.global_progress_index = global_progress_index;
+        upload.startData = 0;
+        upload.addEventListener("progress", progress, false);
+
+        // Allow url to be a method
+        if (jQuery.isFunction(opts.url)) {
+            xhr.open(opts.requestType, opts.url(), true);
+        } else {
+            xhr.open(opts.requestType, opts.url, true);
+        }
+
+        xhr.setRequestHeader('content-type', 'multipart/form-data; boundary=' + boundary);
+        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+
+        // Add headers
+        $.each(opts.headers, function(k, v) {
+          xhr.setRequestHeader(k, v);
+        });
+
+        xhr.send(builder);
+
+        global_progress[global_progress_index] = 0;
+        globalProgress();
+
+        opts.uploadStarted(index, file, files_count, xhr);
+
+        var afterComplete = function(result) {
+          filesDone++;
+
+          // Remove from processing queue
+          processingQueue.forEach(function(value, key) {
+            if (value === fileIndex) {
+              processingQueue.splice(key, 1);
+            }
+          });
+
+          // Add to donequeue
+          doneQueue.push(fileIndex);
+
+          // Make sure the global progress is updated
+          global_progress[global_progress_index] = 100;
+          globalProgress();
+
+          if (filesDone === (files_count - filesRejected)) {
+            afterAll();
+          }
+          if (result === false) {
+            stop_loop = true;
+          }
+        };
+
+        xhr.onabort = afterComplete;
+        xhr.onload = function() {
+            var serverResponse = null;
+
+            if (xhr.responseText) {
+              try {
+                serverResponse = jQuery.parseJSON(xhr.responseText);
+              }
+              catch (e) {
+                serverResponse = xhr.responseText;
+              }
+            }
+
+            var now = new Date().getTime(),
+                timeDiff = now - start_time,
+                result = opts.uploadFinished(index, file, serverResponse, timeDiff, xhr);
+
+            afterComplete(result);
+
+          // Pass any errors to the error option
+          if (xhr.status < 200 || xhr.status > 299) {
+            opts.error(xhr.statusText, file, fileIndex, xhr.status);
+          }
+        };
+      };
+
+      // Initiate the processing loop
+      process();
+    }
+
+    function getIndexBySize(size) {
+      for (var i = 0; i < files_count; i++) {
+        if (files[i].size === size) {
+          return i;
+        }
+      }
+
+      return undefined;
+    }
+
+    function rename(name) {
+      return opts.rename(name);
+    }
+
+    function beforeEach(file) {
+      return opts.beforeEach(file);
+    }
+
+    function afterAll() {
+      return opts.afterAll();
+    }
+
+    function dragEnter(e) {
+      clearTimeout(doc_leave_timer);
+      e.preventDefault();
+      opts.dragEnter.call(this, e);
+    }
+
+    function dragOver(e) {
+      clearTimeout(doc_leave_timer);
+      e.preventDefault();
+      opts.docOver.call(this, e);
+      opts.dragOver.call(this, e);
+    }
+
+    function dragLeave(e) {
+      clearTimeout(doc_leave_timer);
+      opts.dragLeave.call(this, e);
+      e.stopPropagation();
+    }
+
+    function docDrop(e) {
+      e.preventDefault();
+      opts.docLeave.call(this, e);
+      return false;
+    }
+
+    function docEnter(e) {
+      clearTimeout(doc_leave_timer);
+      e.preventDefault();
+      opts.docEnter.call(this, e);
+      return false;
+    }
+
+    function docOver(e) {
+      clearTimeout(doc_leave_timer);
+      e.preventDefault();
+      opts.docOver.call(this, e);
+      return false;
+    }
+
+    function docLeave(e) {
+      doc_leave_timer = setTimeout((function(_this) {
+        return function() {
+          opts.docLeave.call(_this, e);
+        };
+      })(this), 200);
+    }
+
+    return this;
+  };
+
+  function empty() {}
+
+  try {
+    if (XMLHttpRequest.prototype.sendAsBinary) {
+        return;
+    }
+    XMLHttpRequest.prototype.sendAsBinary = function(datastr) {
+      function byteValue(x) {
+        return x.charCodeAt(0) & 0xff;
+      }
+      var ords = Array.prototype.map.call(datastr, byteValue);
+      var ui8a = new Uint8Array(ords);
+
+      // Not pretty: Chrome 22 deprecated sending ArrayBuffer, moving instead
+      // to sending ArrayBufferView.  Sadly, no proper way to detect this
+      // functionality has been discovered.  Happily, Chrome 22 also introduced
+      // the base ArrayBufferView class, not present in Chrome 21.
+      if ('ArrayBufferView' in window)
+        this.send(ui8a);
+      else
+        this.send(ui8a.buffer);
+    };
+  } catch (e) {}
+
+})(jQuery);
diff --git a/js/jquery.multifile.js b/js/jquery.multifile.js
deleted file mode 100644
index a1ae5a02fd9eb4e7f54c53c60951933ecf7fb801..0000000000000000000000000000000000000000
--- a/js/jquery.multifile.js
+++ /dev/null
@@ -1,204 +0,0 @@
-/*********************************************************************
-    jquery.multifile.js
-
-    Multifile plugin that allows users to upload multiple files at once in unobstructive manner - cleaner interface.
-
-    Allows limiting number of files
-    Whitelist file type(s) using file extension
-    Limit file sizes.
-
-    NOTE:
-    * Files are not uploaded until the form is submitted
-    * Server side file type validation is a MUST
-    * Plugin doesn't take into account PHP related limitations e.g max uploads + max size.
-
-    Peter Rotich <peter@osticket.com>
-    Copyright (c) 2006-2013 osTicket
-    http://www.osticket.com
-
-    Credits:
-    The plugin borrows heavily from a plugin by Rocky Meza @ fusionbox
-    https://github.com/fusionbox/jquery-multifile
-
-    vim: expandtab sw=4 ts=4 sts=4:
-**********************************************************************/
-
-;(function($, global, undefined) {
-        
-    $.fn.multifile = function(options) {
-        var container = null;
-        var options = $.extend({}, $.fn.multifile.defaults, options);
-
-        options.allowedFileTypes = $.map(options.file_types.toLowerCase().split(','), $.trim);
-        options.inputTemplate = options.inputTemplate || $.fn.multifile.inputTemplate;
-
-        container = options.container || null;
-
-
-        return this.each(function() {
-
-            var settings = options;
-            var $container
-                
-            , addInput = function(event) {
-            
-                var $this = $(this)
-                , fObj = $(this).closest('form')
-                , new_input = $.fn.multifile.cloneInput($this)
-                , file = $.fn.multifile.getFileObject(this);
-
-                if(fObj.data('files') == undefined)
-                    fObj.data('files', 0);
-
-                if(fObj.data('files')>=settings.max_uploads || (fObj.data('files')+file.count)>settings.max_uploads) {
-                    alert('You have reached the maximum number of files ('+ settings.max_uploads+') allowed per upload');
-                } else if(!$.fn.multifile.checkFileTypes(file, settings.allowedFileTypes)) {
-                    var msg = 'Selected file type is NOT allowed';
-                    if(file.count>1)
-                        msg = 'File type of one or more of the selected files is NOT allowed';
-
-                    alert('Error: '+msg);
-                    $this.replaceWith(new_input);
-                } else if(!$.fn.multifile.checkFileSize(file, settings.max_file_size)) {
-                    var msg = 'Selected file exceeds allowed size';
-                    if(file.count>1)
-                        msg = 'File size of one or more of the selected files exceeds allowed size';
-
-                    alert('Error: '+msg);
-                    $this.replaceWith(new_input);
-                } else {
-                    $this.hide();
-                    
-                    settings
-                    .inputTemplate(file)
-                    .appendTo($container)
-                    .on('click', 'input',  bindRemoveInput($this, file));
-
-                    fObj.data('files', fObj.data('files')+file.count);
-                    if(fObj.data('files')<settings.max_uploads)
-                        $this.after(new_input);
-                }
-        
-            }
-      
-            , bindRemoveInput = function($input, file) {
-
-                return function(event) {
-
-                    event.preventDefault();
-           
-                    if(confirm('Are you sure you want to remove '+file.name+'?')) {
-                        var fObj = $(this).closest('form');
-
-                        fObj.data('files', fObj.data('files')-file.count);
-                        if(fObj.data('files')<settings.max_uploads && (fObj.data('files')+file.count)>=settings.max_uploads)
-                            $input.after($.fn.multifile.cloneInput($input).show());
-                        
-                        $input.remove();
-                        $(this).parent().remove();
-                    }
-
-                    return false;
-                };
-        
-            };
-    
-            if ( container ) {
-                if ( typeof container == 'string' ) 
-                    $container = $(container, $(this).closest('form'));
-                else
-                    $container = container;
-            } else {
-                $container = $('<div class="uploads" />');
-                $(this).after($container);
-            }
-
-            $(this).bind('change.multifile', addInput);
-
-        });
-  };
-
-  $.fn.multifile.inputTemplate = function(file) {
-    return $('<label style="padding-right:5px;"><input type="checkbox" name="uploads[]" value="' + file.name + '" checked="checked"> ' + file.name + '</label><br/>');
-  };
-
-  $.fn.multifile.checkFileTypes = function(file, allowedFileTypes) {
-
-      //Wildcard.
-      if(allowedFileTypes[0]=='.*')
-          return true;
-
-      var filenames = $.map(file.name.toLowerCase().split(','), $.trim);
-      for (var i = 0, _len = filenames.length; i < _len; i++)
-          if(filenames[i] && $.inArray('.'+filenames[i].split('.').pop(), allowedFileTypes) == -1)
-              return false;
-
-      return true;
-  };  
-  
-  $.fn.multifile.checkFileSize = function(file, MaxFileSize) {
-
-      //Size info not available or max file is not set (let server-side handle it).
-      if(!MaxFileSize || !file.size)
-          return true;
-     
-      var filesizes = $.map(file.size.split(','), $.trim);
-      for (var i = 0, _len = filesizes.length; i < _len; i++)
-          if(filesizes[i] > MaxFileSize)
-              return false;
-
-      return true;
-  };
-
-  //Clone file input and clear the value without triggering a warning!
-  $.fn.multifile.cloneInput = function(input) {
-
-      var $clone = input.clone(true);
-                      
-      if ($.browser.msie) {
-          $clone.replaceWith(function () { return $(this).clone(true); });
-      } else {
-          $clone.val('');
-      }
-
-      return $clone;
-  }
-
-  //Get file object 
-  $.fn.multifile.getFileObject = function(input) {
-    var file = {};
-
-    file.count = 1; 
-    // check for HTML5 FileList support
-    if ( !!global.FileList ) {
-      if ( input.files.length == 1 ) {
-        file.name = input.files[0].name;
-        file.size = '' + input.files[0].size;
-      } else { //Multi-select
-        // We do this in order to support `multiple` files.
-        // You can't display them separately because they 
-        // belong to only one file input.  It is impossible
-        // to remove just one of the files.
-        file.name = input.files[0].name;
-        file.size = '' + input.files[0].size;
-        for (var i = 1, _len = input.files.length; i < _len; i++) {
-          file.name += ', ' + input.files[i].name;
-          file.size += ', ' + input.files[i].size;
-        }
-
-        file.count = i;
-      }
-    } else {
-      file.name = input.value;
-    }
-
-    return file;
-  };
-
-  //Default options 
-  $.fn.multifile.defaults = { 
-                              max_uploads: 1,
-                              file_types: '.*',
-                              max_file_size: 0
-                            };
-})(jQuery, this);
diff --git a/js/jquery.pjax.js b/js/jquery.pjax.js
index e2f958716ed658d8a1e6ee0563d08cf7a7820b8c..7c1d3535e1028a27a7fbe3d5d4825567116a4399 100644
--- a/js/jquery.pjax.js
+++ b/js/jquery.pjax.js
@@ -263,7 +263,9 @@ function pjax(options) {
     }
 
     // Clear out any focused controls before inserting new page contents.
-    document.activeElement.blur()
+    try {
+      document.activeElement.blur()
+    } catch (e) { }
 
     if (container.title) document.title = container.title
     context.html(container.contents)
diff --git a/js/osticket.js b/js/osticket.js
index 7cd637fae03b0d92fc1f925b664e36a636f58818..f4aeebd1a1075541fadb34c70f4f49ce6fd70452 100644
--- a/js/osticket.js
+++ b/js/osticket.js
@@ -27,7 +27,7 @@ $(document).ready(function(){
             fObj.data('changed', true);
             $('input[type=submit]', fObj).css('color', 'red');
             $(window).bind('beforeunload', function(e) {
-                return "Are you sure you want to leave? Any changes or info you've entered will be discarded!";
+                return __("Are you sure you want to leave? Any changes or info you've entered will be discarded!");
              });
         }
        });
@@ -94,20 +94,6 @@ $(document).ready(function(){
         }
     })();
 
-    /* Multifile uploads */
-    var elems = $('.multifile');
-    if (elems.exists()) {
-        /* Get config settings from the backend */
-        getConfig().then(function(c) {
-            elems.multifile({
-                container:   '.uploads',
-                max_uploads: c.max_file_uploads || 1,
-                file_types:  c.file_types || ".*",
-                max_file_size: c.max_file_size || 0
-            });
-        });
-    }
-
     $.translate_format = function(str) {
         var translation = {
             'd':'dd',
@@ -146,9 +132,9 @@ $(document).ready(function(){
         if (!extra) return;
         if (!imgs.length) return;
         extra.append($('<a>')
-          .addClass("action-button show-images")
+          .addClass("action-button show-images pull-right")
           .css({'font-weight':'normal'})
-          .text(' Show Images')
+          .text(' ' + __('Show Images'))
           .click(function(ev) {
             imgs.each(function(i, img) {
               showNonLocalImage(img);
@@ -208,13 +194,33 @@ showImagesInline = function(urls, thread_id) {
                     }
                 ).append($('<div class="caption">')
                     .append('<span class="filename">'+info.filename+'</span>')
-                    .append('<a href="'+info.download_url+'" class="action-button"><i class="icon-download-alt"></i> Download</a>')
+                    .append('<a href="'+info.download_url+'" class="action-button pull-right"><i class="icon-download-alt"></i> ' + __('Download') + '</a>')
                 );
             e.data('wrapped', true);
         }
     });
 }
 
+$.sysAlert = function (title, msg, cb) {
+    var $dialog =  $('.dialog#alert');
+    if ($dialog.length) {
+        $('#title', $dialog).html(title);
+        $('#body', $dialog).html(msg);
+        $dialog.show();
+    } else {
+        msg = msg.replace(/<br\s*\/?>/, "\n").replace(/<\/?\w+[^>]*>/g, '');
+        alert(title+':\n'+msg);
+    }
+};
+
+function __(s) {
+  if ($.oststrings && $.oststrings[s])
+    return $.oststrings[s];
+  return s;
+}
+
+$.clientPortal = true;
+
 $(document).on('submit', 'form', function() {
     // Reformat dates
     $('.dp', $(this)).each(function(i, e) {
diff --git a/js/redactor-fonts.js b/js/redactor-fonts.js
index fc033523a3e16b7798b29fcde743b9c9a2a5974e..021ca561d63b28bd5e38b8c4f1c4ef8bdc6bfe57 100644
--- a/js/redactor-fonts.js
+++ b/js/redactor-fonts.js
@@ -3,7 +3,7 @@ if (!RedactorPlugins) var RedactorPlugins = {};
 RedactorPlugins.fontfamily = {
     init: function ()
     {
-        var fonts = [ 'Arial', 'Helvetica', 'Georgia', 'Times New Roman', 'Monospace' ];
+        var fonts = [ 'Arial', 'Helvetica', 'Georgia', 'Times New Roman', __('Monospace') ];
         var that = this;
         var dropdown = {};
 
@@ -14,7 +14,7 @@ RedactorPlugins.fontfamily = {
 
         dropdown['remove'] = { title: 'Remove font', callback: function() { that.resetFontfamily(); }};
 
-        this.buttonAddBefore('bold', 'fontfamily', 'Change font family', false, dropdown);
+        this.buttonAddBefore('bold', 'fontfamily', __('Change font family'), false, dropdown);
     },
     setFontfamily: function (value)
     {
@@ -120,9 +120,9 @@ RedactorPlugins.fontsize = {
                 callback: function() { that.setFontsize(s); } };
 		});
 
-		dropdown['remove'] = { title: 'Remove font size', callback: function() { that.resetFontsize(); } };
+		dropdown['remove'] = { title: __('Remove font size'), callback: function() { that.resetFontsize(); } };
 
-		this.buttonAddAfter('formatting', 'fontsize', 'Change font size', false, dropdown);
+		this.buttonAddAfter('formatting', 'fontsize', __('Change font size'), false, dropdown);
 	},
 	setFontsize: function(size)
 	{
@@ -133,3 +133,55 @@ RedactorPlugins.fontsize = {
 		this.inlineRemoveStyle('font-size');
 	}
 };
+
+RedactorPlugins.textdirection = {
+    init: function()
+    {
+        var that = this;
+        var dropdown = {};
+
+        dropdown.ltr = { title: __('Left to Right'), callback: this.setLtr };
+        dropdown.rtl = { title: __('Right to Left'), callback: this.setRtl };
+
+        var button = this.buttonAdd('textdirection', __('Change Text Direction'),
+            false, dropdown);
+
+        if (this.opts.direction == 'rtl')
+            this.setRtl();
+    },
+    setRtl: function()
+    {
+        var c = this.getCurrent(), s = this.getSelection();
+        this.bufferSet();
+        if (s.type == 'Range' && s.focusNode.nodeName != 'div') {
+            this.linebreakHack(s);
+        }
+        else if (!c) {
+            var repl = '<div dir="rtl">' + this.get() + '</div>';
+            this.set(repl, false);
+        }
+        $(this.getCurrent()).attr('dir', 'rtl');
+        this.sync();
+    },
+    setLtr: function()
+    {
+        var c = this.getCurrent(), s = this.getSelection();
+        this.bufferSet();
+        if (s.type == 'Range' && s.focusNode.nodeName != 'div') {
+            this.linebreakHack(s);
+        }
+        else if (!c) {
+            var repl = '<div dir="ltr">' + this.get() + '</div>';
+            this.set(repl, false);
+        }
+        $(this.getCurrent()).attr('dir', 'ltr');
+        this.sync();
+    },
+    linebreakHack: function(sel) {
+        var range = sel.getRangeAt(0);
+        var wrapper = document.createElement('div');
+        wrapper.appendChild(range.extractContents());
+        range.insertNode(wrapper);
+        this.selectionElement(wrapper);
+    }
+};
diff --git a/js/redactor-osticket.js b/js/redactor-osticket.js
index b181aeca42d4abda3793a0c671fe9d2d64519822..cded2e1f3ffdc7d425428155eec3cf2a0815777d 100644
--- a/js/redactor-osticket.js
+++ b/js/redactor-osticket.js
@@ -28,14 +28,13 @@ RedactorPlugins.draft = {
 
         this.$draft_saved = $('<span>')
             .addClass("pull-right draft-saved")
-            .css({'position':'absolute','top':'3em','right':'0.5em'})
             .hide()
             .append($('<span>')
-                .text('Draft Saved'));
+                .text(__('Draft Saved')));
         // Float the [Draft Saved] box with the toolbar
         this.$toolbar.append(this.$draft_saved);
         if (this.opts.draftDelete) {
-            var trash = this.buttonAdd('deleteDraft', 'Delete Draft', this.deleteDraft);
+            var trash = this.buttonAdd('deleteDraft', __('Delete Draft'), this.deleteDraft);
             this.buttonAwesome('deleteDraft', 'icon-trash');
             trash.parent().addClass('pull-right');
             trash.addClass('delete-draft');
@@ -95,20 +94,10 @@ RedactorPlugins.draft = {
         $('input[name=draft_id]', this.$box.closest('form'))
             .val(data.draft_id);
         this.draft_id = data.draft_id;
-
-        var self = this;
-        getConfig().then(function(c) {
-            if (c.allow_attachments) {
-                self.opts.clipboardUploadUrl =
-                self.opts.imageUpload =
-                    'ajax.php/draft/'+data.draft_id+'/attach';
-                self.opts.imageUploadErrorCallback = self.displayError;
-                // XXX: This happens in ::buildBindKeyboard() from
-                // ::buildAfter(). However, the imageUpload option is not
-                // known when the Redactor is init()'d
-                self.$editor.on('drop.redactor', $.proxy(self.buildEventDrop, self));
-            }
-        });
+        this.opts.clipboardUploadUrl =
+        this.opts.imageUpload =
+            'ajax.php/draft/'+data.draft_id+'/attach';
+        this.opts.imageUploadErrorCallback = this.displayError;
         this.opts.original_autosave = this.opts.autosave;
         this.opts.autosave = 'ajax.php/draft/'+data.draft_id;
     },
@@ -177,6 +166,7 @@ RedactorPlugins.signature = {
                 }, 'fast');
                 $(this).css('box-shadow', originalShadow);
             });
+            this.$box.find('.redactor_editor').css('border-bottom-style', 'none', true);
         }
     },
     updateSignature: function(e) {
@@ -224,7 +214,12 @@ $(function() {
     },
     redact = $.redact = function(el, options) {
         var el = $(el),
-            options = $.extend({
+            sizes = {'small': 75, 'medium': 150, 'large': 225},
+            selectedSize = sizes['medium'];
+        $.each(sizes, function(k, v) {
+            if (el.hasClass(k)) selectedSize = v;
+        });
+        var options = $.extend({
                 'air': el.hasClass('no-bar'),
                 'airButtons': ['formatting', '|', 'bold', 'italic', 'underline', 'deleted', '|', 'unorderedlist', 'orderedlist', 'outdent', 'indent', '|', 'image'],
                 'buttons': ['html', '|', 'formatting', '|', 'bold',
@@ -233,9 +228,9 @@ $(function() {
                     'file', 'table', 'link', '|', 'alignment', '|',
                     'horizontalrule'],
                 'autoresize': !el.hasClass('no-bar'),
-                'minHeight': el.hasClass('small') ? 75 : 150,
+                'minHeight': selectedSize,
                 'focus': false,
-                'plugins': ['fontcolor','fontfamily', 'signature'],
+                'plugins': [],
                 'imageGetJson': 'ajax.php/draft/images/browse',
                 'syncBeforeCallback': captureImageSizes,
                 'linebreaks': true,
@@ -262,12 +257,25 @@ $(function() {
             // the image was inserted.
             el.redactor('sync');
         });
+        if (!$.clientPortal) {
+            options['plugins'] = options['plugins'].concat(
+                    'fontcolor', 'fontfamily', 'signature');
+        }
         if (el.hasClass('draft')) {
             el.closest('form').append($('<input type="hidden" name="draft_id"/>'));
             options['plugins'].push('draft');
             options.draftDelete = el.hasClass('draft-delete');
         }
-        el.redactor(options);
+        getConfig().then(function(c) {
+            if (c.lang && c.lang.toLowerCase() != 'en_us' &&
+                    $.Redactor.opts.langs[c.short_lang])
+                options['lang'] = c.short_lang;
+            if (c.has_rtl)
+                options['plugins'].push('textdirection');
+            if ($('html.rtl').length)
+                options['direction'] = 'rtl';
+            el.redactor(options);
+        });
     },
     findRichtextBoxes = function() {
         $('.richtext').each(function(i,el) {
@@ -306,7 +314,7 @@ $(document).ajaxError(function(event, request, settings) {
             }
         });
         $('#overlay').show();
-        alert('Unable to save draft. Refresh the current page to restore and continue your draft.');
+        alert(__('Unable to save draft. Refresh the current page to restore and continue your draft.'));
         $('#overlay').hide();
     }
 });
diff --git a/kb/faq.php b/kb/faq.php
index da5a8fa98aa39ce28f21dac0c9b339f3f0140b5a..529535cd9c39ec464bf5a87f2bcefae724d2b04d 100644
--- a/kb/faq.php
+++ b/kb/faq.php
@@ -18,10 +18,10 @@ require_once(INCLUDE_DIR.'class.faq.php');
 
 $faq=$category=null;
 if($_REQUEST['id'] && !($faq=FAQ::lookup($_REQUEST['id'])))
-   $errors['err']='Unknown or invalid FAQ';
+   $errors['err']=sprintf(__('%s: Unknown or invalid'), __('FAQ article'));
 
 if(!$faq && $_REQUEST['cid'] && !($category=Category::lookup($_REQUEST['cid'])))
-    $errors['err']='Unknown or invalid FAQ category';
+    $errors['err']=sprintf(__('%s: Unknown or invalid'), __('FAQ category'));
 
 
 $inc='knowledgebase.inc.php'; //FAQs landing page.
diff --git a/kb/file.php b/kb/file.php
index b06b256a35a6ebdd9c40137f9fec0b6a2f797343..99664708a567a08d80eaca73ece27a7f9973f87a 100644
--- a/kb/file.php
+++ b/kb/file.php
@@ -1,9 +1,9 @@
 <?php
 /*********************************************************************
     file.php
-    
+
     Simply downloads the file...on hash validation as follows;
-    
+
     * Hash must be 64 chars long.
     * First 32 chars is the perm. file hash
     * Next 32 chars  is md5(file_id.session_id().file_hash)
@@ -24,7 +24,7 @@ $h=trim($_GET['h']);
 if(!$h  || strlen($h)!=64  //32*2
         || !($file=AttachmentFile::lookup(substr($h,0,32))) //first 32 is the file hash.
         || strcasecmp($h, $file->getDownloadHash())) //next 32 is file id + session hash.
-    die('Unknown or invalid file. #'.Format::htmlchars($_GET['h']));
+    Http::response(404, __('Unknown or invalid file'));
 
 $file->download();
 ?>
diff --git a/l.php b/l.php
index b6a47ff84cdd95ed8501556cc75a8f73b63a2258..a0520a8cbba31e26cd743e3e9546aebd329e0131 100644
--- a/l.php
+++ b/l.php
@@ -18,11 +18,11 @@ require 'secure.inc.php';
 
 # PHP < 5.4.7 will not handle a URL like //host.tld/path correctly
 if (!($url=trim($_GET['url'])))
-    Http::response(422, 'Invalid URL');
+    Http::response(422, __('Invalid URL'));
 
 $check = (strpos($url, '//') === 0) ? 'http:' . $url : $url;
 if (!Validator::is_url($check) || !$ost->validateLinkToken($_GET['auth']))
-    Http::response(403, 'URL link not authorized');
+    Http::response(403, __('URL link not authorized'));
 elseif (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === false)
     Http::redirect($url);
 ?>
diff --git a/login.php b/login.php
index 11c09ec3470f84235a7191c23e832f963769d0ab..f79bf8563320f672fdb90c67fff9e178eafd4fd7 100644
--- a/login.php
+++ b/login.php
@@ -33,7 +33,7 @@ else
 $suggest_pwreset = false;
 if ($_POST && isset($_POST['luser'])) {
     if (!$_POST['luser'])
-        $errors['err'] = 'Valid username or email address is required';
+        $errors['err'] = __('Valid username or email address is required');
     elseif (($user = UserAuthenticationBackend::process($_POST['luser'],
             $_POST['lpasswd'], $errors))) {
         if ($user instanceof ClientCreateRequest) {
@@ -47,8 +47,7 @@ if ($_POST && isset($_POST['luser'])) {
                 $user_form = UserForm::getUserForm()->getForm($user->getInfo());
             }
             else {
-                $errors['err'] = 'Access Denied. Contact your help desk
-                    administrator to have an account registered for you';
+                $errors['err'] = __('Access Denied. Contact your help desk administrator to have an account registered for you');
                 // fall through to show login page again
             }
         }
@@ -57,13 +56,13 @@ if ($_POST && isset($_POST['luser'])) {
                 ?: 'tickets.php');
         }
     } elseif(!$errors['err']) {
-        $errors['err'] = 'Invalid username or password - try again!';
+        $errors['err'] = __('Invalid username or password - try again!');
     }
     $suggest_pwreset = true;
 }
 elseif ($_POST && isset($_POST['lticket'])) {
     if (!Validator::is_email($_POST['lemail']))
-        $errors['err'] = 'Valid email address and ticket number required';
+        $errors['err'] = __('Valid email address and ticket number required');
     elseif (($user = UserAuthenticationBackend::process($_POST['lemail'],
             $_POST['lticket'], $errors))) {
 
@@ -75,11 +74,11 @@ elseif ($_POST && isset($_POST['lticket'])) {
         // We're using authentication backend so we can guard aganist brute
         // force attempts (which doesn't buy much since the link is emailed)
         $user->sendAccessLink();
-        $msg = sprintf("%s - access link sent to your email!",
+        $msg = sprintf(__("%s - access link sent to your email!"),
             Format::htmlchars($user->getName()->getFirst()));
         $_POST = null;
     } elseif(!$errors['err']) {
-        $errors['err'] = 'Invalid email or ticket number - try again!';
+        $errors['err'] = __('Invalid email or ticket number - try again!');
     }
 }
 elseif (isset($_GET['do'])) {
@@ -106,8 +105,7 @@ elseif ($user = UserAuthenticationBackend::processSignOn($errors, false)) {
             $inc = 'register.inc.php';
         }
         else {
-            $errors['err'] = 'Access Denied. Contact your help desk
-                administrator to have an account registered for you';
+            $errors['err'] = __('Access Denied. Contact your help desk administrator to have an account registered for you');
             // fall through to show login page again
         }
     }
diff --git a/main.inc.php b/main.inc.php
index 7feb271e7743274b02b2227ac29e68f4e67f3f7e..026e440ca84cc904d8a5ebcf8e6b5101cd81d41e 100644
--- a/main.inc.php
+++ b/main.inc.php
@@ -28,7 +28,7 @@ Bootstrap::loadCode();
 Bootstrap::connect();
 
 if(!($ost=osTicket::start()) || !($cfg = $ost->getConfig()))
-Bootstrap::croak('Unable to load config info from DB. Get tech support.');
+Bootstrap::croak(__('Unable to load config info from DB. Get tech support.'));
 
 //Init
 $session = $ost->getSession();
@@ -43,4 +43,12 @@ $_POST=Format::strip_slashes($_POST);
 $_GET=Format::strip_slashes($_GET);
 $_REQUEST=Format::strip_slashes($_REQUEST);
 }
+
+// extract system messages
+$errors = array();
+$msg=$warn=$sysnotice='';
+if ($_SESSION['::sysmsgs']) {
+    extract($_SESSION['::sysmsgs']);
+    unset($_SESSION['::sysmsgs']);
+}
 ?>
diff --git a/offline.php b/offline.php
index 04323f9e0a86d6831b6e6149ed65f3933fa88ed1..0e284a858218162e3d6b0524b6403ed4b6632d3d 100644
--- a/offline.php
+++ b/offline.php
@@ -27,7 +27,7 @@ require(CLIENTINC_DIR.'header.inc.php');
 if(($page=$cfg->getOfflinePage())) {
     echo $page->getBody();
 } else {
-    echo '<h1>Support Ticket System Offline</h1>';
+    echo '<h1>'.__('Support Ticket System Offline').'</h1>';
 }
 ?>
 </div>
diff --git a/open.php b/open.php
index 9201d15c9d18c4e67b3349904ac69efa11970159..2311bc2bc57759bbcd812cdb75ace610fc504439 100644
--- a/open.php
+++ b/open.php
@@ -24,20 +24,23 @@ if ($_POST) {
         $vars['uid']=$thisclient->getId();
     } elseif($cfg->isCaptchaEnabled()) {
         if(!$_POST['captcha'])
-            $errors['captcha']='Enter text shown on the image';
+            $errors['captcha']=__('Enter text shown on the image');
         elseif(strcmp($_SESSION['captcha'], md5(strtoupper($_POST['captcha']))))
-            $errors['captcha']='Invalid - try again!';
+            $errors['captcha']=__('Invalid - try again!');
     }
 
-    if (!$errors && $cfg->allowOnlineAttachments() && $_FILES['attachments'])
-        $vars['files'] = AttachmentFile::format($_FILES['attachments'], true);
+    $tform = TicketForm::objects()->one()->getForm($vars);
+    $messageField = $tform->getField('message');
+    $attachments = $messageField->getWidget()->getAttachments();
+    if (!$errors && $messageField->isAttachmentsEnabled())
+        $vars['cannedattachments'] = $attachments->getClean();
 
     // Drop the draft.. If there are validation errors, the content
     // submitted will be displayed back to the user
     Draft::deleteForNamespace('ticket.client.'.substr(session_id(), -12));
     //Ticket::create...checks for errors..
     if(($ticket=Ticket::create($vars, $errors, SOURCE))){
-        $msg='Support ticket request created';
+        $msg=__('Support ticket request created');
         // Drop session-backed form data
         unset($_SESSION[':form-data']);
         //Logged in...simply view the newly created ticket.
@@ -47,7 +50,7 @@ if ($_POST) {
             @header('Location: tickets.php?id='.$ticket->getId());
         }
     }else{
-        $errors['err']=$errors['err']?$errors['err']:'Unable to create a ticket. Please correct errors below and try again!';
+        $errors['err']=$errors['err']?$errors['err']:__('Unable to create a ticket. Please correct errors below and try again!');
     }
 }
 
diff --git a/pages/index.php b/pages/index.php
index f646858352e9cb8e34b16300f9e6713b6930f392..ab50a1c586d5793301fdcb0246842c31eba95ec2 100644
--- a/pages/index.php
+++ b/pages/index.php
@@ -41,10 +41,10 @@ while (list($id, $name) = db_fetch_row($res)) {
 }
 
 if (!$page_id || !($page = Page::lookup($page_id)))
-    Http::response(404, 'Page Not Found');
+    Http::response(404, __('Page Not Found'));
 
 if (!$page->isActive() || $page->getType() != 'other')
-    Http::response(404, 'Page Not Found');
+    Http::response(404, __('Page Not Found'));
 
 require(CLIENTINC_DIR.'header.inc.php');
 
diff --git a/pwreset.php b/pwreset.php
index 2b63aabf63477ab6818d965138b548d244aa9630..e37e4d264bab3aa20484be28a65613d63e6dcd5d 100644
--- a/pwreset.php
+++ b/pwreset.php
@@ -10,25 +10,24 @@ require_once(INCLUDE_DIR.'class.client.php');
 $inc = 'pwreset.request.php';
 if($_POST) {
     if (!$ost->checkCSRFToken()) {
-        Http::response(400, 'Valid CSRF Token Required');
+        Http::response(400, __('Valid CSRF Token Required'));
         exit;
     }
     switch ($_POST['do']) {
         case 'sendmail':
             if (($acct=ClientAccount::lookupByUsername($_POST['userid']))) {
                 if (!$acct->isPasswdResetEnabled()) {
-                    $banner = 'Password reset is not enabled for your account. '
-                        .'Contact your administrator';
+                    $banner = __('Password reset is not enabled for your account. Contact your administrator');
                 }
                 elseif ($acct->sendResetEmail()) {
                     $inc = 'pwreset.sent.php';
                 }
                 else
-                    $banner = 'Unable to send reset email. Internal error';
+                    $banner = __('Unable to send reset email. Internal error');
             }
             else
-                $banner = 'Unable to verify username '
-                    .Format::htmlchars($_POST['userid']);
+                $banner = sprintf(__('Unable to verify username: %s'),
+                    Format::htmlchars($_POST['userid']));
             break;
         case 'reset':
             $inc = 'pwreset.login.php';
@@ -43,7 +42,7 @@ if($_POST) {
     }
 }
 elseif ($_GET['token']) {
-    $banner = 'Re-enter your username or email';
+    $banner = __('Re-enter your username or email');
     $inc = 'pwreset.login.php';
     $_config = new Config('pwreset');
     if (($id = $_config->get($_GET['token']))
@@ -71,10 +70,10 @@ elseif ($_GET['token']) {
         Http::redirect('index.php');
 }
 elseif ($cfg->allowPasswordReset()) {
-    $banner = 'Enter your username or email address below';
+    $banner = __('Enter your username or email address below');
 }
 else {
-    $_SESSION['_staff']['auth']['msg']='Password resets are disabled';
+    $_SESSION['_staff']['auth']['msg']=__('Password resets are disabled');
     return header('Location: index.php');
 }
 
diff --git a/scp/admin.inc.php b/scp/admin.inc.php
index 97dc168406af6c36909cdcfd135eca8947fc5624..434589acaa0584d7164fa32afc48c6848adea88c 100644
--- a/scp/admin.inc.php
+++ b/scp/admin.inc.php
@@ -20,6 +20,7 @@ if(!$ost or !$thisstaff or !$thisstaff->isAdmin()){
     require('index.php'); // just in case!
     exit;
 }
+
 //Define some constants.
 define('OSTADMININC',TRUE); //checked by admin include files
 define('ADMINPAGE',TRUE);   //Used by the header to swap menus.
@@ -27,7 +28,7 @@ define('ADMINPAGE',TRUE);   //Used by the header to swap menus.
 //Some security related warnings - bitch until fixed!!! :)
 $sysnotice= '';
 if($ost->isUpgradePending()) {
-    $errors['err']=$sysnotice='System upgrade is pending <a href="upgrade.php">Upgrade Now</a>';
+    $errors['err']=$sysnotice=__('System upgrade is pending').' <a href="upgrade.php">'.__('Upgrade Now').'</a>';
     if(!in_array(basename($_SERVER['SCRIPT_NAME']), array('upgrade.php', 'logs.php'))) {
         header('Location: upgrade.php');
         require('upgrade.php');
@@ -36,26 +37,26 @@ if($ost->isUpgradePending()) {
 } else {
 
     if(!strcasecmp(basename(CONFIG_FILE), 'settings.php')) {
-        $sysnotice=sprintf('Please rename config file include/%s to include/ost-config.php to avoid possible conflicts',
+        $sysnotice=sprintf(__('Please rename config file include/%s to include/ost-config.php to avoid possible conflicts'),
                                 basename(CONFIG_FILE));
         //Die gracefully - otherwise upgraded RC5 installations will die with confusing message.
         if(!strcasecmp(basename($_SERVER['SCRIPT_NAME']), 'settings.php'))
             die($sysnotice);
 
     } elseif(file_exists('../setup/')) {
-        $sysnotice='Please take a minute to delete <strong>setup/install</strong> directory (../setup/) for security reasons.';
+        $sysnotice=__('Please take a minute to delete <strong>setup/install</strong> directory (../setup/) for security reasons.');
     } elseif(CONFIG_FILE && file_exists(CONFIG_FILE) && is_writable(CONFIG_FILE)) {
             //Confirm for real that the file is writable by group or world.
             clearstatcache(); //clear the cache!
             $perms = @fileperms(CONFIG_FILE);
             if(($perms & 0x0002) || ($perms & 0x0010)) {
-                $sysnotice=sprintf('Please change permission of config file (%s) to remove write access. e.g <i>chmod 644 %s</i>',
+                $sysnotice=sprintf(__('Please change permission of config file (%1$s) to remove write access. e.g <i>chmod 644 %2$s</i>'),
                                 basename(CONFIG_FILE), basename(CONFIG_FILE));
             }
     }
 
     if(!$sysnotice && ini_get('register_globals'))
-        $sysnotice='Please consider turning off register globals if possible';
+        $sysnotice=__('Please consider turning off register globals if possible');
 }
 
 //System notice displayed as a warning (if any).
@@ -65,5 +66,5 @@ $ost->setWarning($sysnotice);
 $nav = new AdminNav($thisstaff);
 
 //Page title.
-$ost->setPageTitle('osTicket :: Admin Control Panel');
+$ost->setPageTitle(__('osTicket :: Admin Control Panel'));
 ?>
diff --git a/scp/ajax.php b/scp/ajax.php
index ab7708e0b2974903ef7eae490cbfc66665f4baea..1168513601eb45882783c339934006856b36c550 100644
--- a/scp/ajax.php
+++ b/scp/ajax.php
@@ -55,11 +55,13 @@ $dispatcher = patterns('',
         url_get('^help-topic/(?P<id>\d+)$', 'getFormsForHelpTopic'),
         url_get('^field-config/(?P<id>\d+)$', 'getFieldConfiguration'),
         url_post('^field-config/(?P<id>\d+)$', 'saveFieldConfiguration'),
-        url_delete('^answer/(?P<entry>\d+)/(?P<field>\d+)$', 'deleteAnswer')
+        url_delete('^answer/(?P<entry>\d+)/(?P<field>\d+)$', 'deleteAnswer'),
+        url_post('^upload/(\d+)?$', 'upload'),
+        url_post('^upload/(\w+)?$', 'attach')
     )),
     url('^/list/', patterns('ajax.forms.php:DynamicFormsAjaxAPI',
-        url_get('^item/(?P<id>\d+)/properties$', 'getListItemProperties'),
-        url_post('^item/(?P<id>\d+)/properties$', 'saveListItemProperties')
+        url_get('^(?P<list>\w+)/item/(?P<id>\d+)/properties$', 'getListItemProperties'),
+        url_post('^(?P<list>\w+)/item/(?P<id>\d+)/properties$', 'saveListItemProperties')
     )),
     url('^/report/overview/', patterns('ajax.reports.php:OverviewReportAjaxAPI',
         # Send
@@ -135,11 +137,15 @@ $dispatcher = patterns('',
         url_get('^(?P<tid>\d+)/add-collaborator/(?P<uid>\d+)$', 'addCollaborator'),
         url_get('^(?P<tid>\d+)/add-collaborator/auth:(?P<bk>\w+):(?P<id>.+)$', 'addRemoteCollaborator'),
         url('^(?P<tid>\d+)/add-collaborator$', 'addCollaborator'),
-        url_get('^lookup', 'lookup'),
-        url_get('^search', 'search'),
         url_get('^(?P<tid>\d+)/forms/manage$', 'manageForms'),
         url_post('^(?P<tid>\d+)/forms/manage$', 'updateForms'),
-        url_get('^(?P<tid>\d+)/canned-resp/(?P<cid>\w+).(?P<format>json|txt)', 'cannedResponse')
+        url_get('^(?P<tid>\d+)/canned-resp/(?P<cid>\w+).(?P<format>json|txt)', 'cannedResponse'),
+        url_get('^(?P<tid>\d+)/status/(?P<status>\w+)(?:/(?P<sid>\d+))?$', 'changeTicketStatus'),
+        url_post('^(?P<tid>\d+)/status$', 'setTicketStatus'),
+        url_get('^status/(?P<status>\w+)(?:/(?P<sid>\d+))?$', 'changeSelectedTicketsStatus'),
+        url_post('^status/(?P<state>\w+)$', 'setSelectedTicketsStatus'),
+        url_get('^lookup', 'lookup'),
+        url_get('^search', 'search')
     )),
     url('^/collaborators/', patterns('ajax.tickets.php:TicketsAjaxAPI',
         url_get('^(?P<cid>\d+)/view$', 'viewCollaborator'),
@@ -159,10 +165,18 @@ $dispatcher = patterns('',
         url_delete('^(?P<id>\d+)$', 'deleteNote'),
         url_post('^attach/(?P<ext_id>\w\d+)$', 'createNote')
     )),
+    url('^/sequence/', patterns('ajax.sequence.php:SequenceAjaxAPI',
+        url_get('^(?P<id>\d+)$', 'current'),
+        url_get('^manage$', 'manage'),
+        url_post('^manage$', 'manage')
+    )),
     url_post('^/upgrader', array('ajax.upgrader.php:UpgraderAjaxAPI', 'upgrade')),
     url('^/help/', patterns('ajax.tips.php:HelpTipAjaxAPI',
         url_get('^tips/(?P<namespace>[\w_.]+)$', 'getTipsJson'),
         url_get('^(?P<lang>[\w_]+)?/tips/(?P<namespace>[\w_.]+)$', 'getTipsJsonForLang')
+    )),
+    url('^/i18n/(?P<lang>[\w_]+)/', patterns('ajax.i18n.php:i18nAjaxAPI',
+        url_get('(?P<tag>\w+)$', 'getLanguageFile')
     ))
 );
 
diff --git a/scp/apikeys.php b/scp/apikeys.php
index 529b67a8af1195e18f606f8f1aef37446b2d0c6a..c4de318105f6776937e17e8c1a637cc70302fb4f 100644
--- a/scp/apikeys.php
+++ b/scp/apikeys.php
@@ -18,30 +18,31 @@ include_once(INCLUDE_DIR.'class.api.php');
 
 $api=null;
 if($_REQUEST['id'] && !($api=API::lookup($_REQUEST['id'])))
-    $errors['err']='Unknown or invalid API key ID.';
+    $errors['err']=sprintf(__('%s: Unknown or invalid ID.'), __('API key'));
 
 if($_POST){
     switch(strtolower($_POST['do'])){
         case 'update':
             if(!$api){
-                $errors['err']='Unknown or invalid API key.';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('API key'));
             }elseif($api->update($_POST,$errors)){
-                $msg='API key updated successfully';
+                $msg=sprintf(__('Succesfully updated %s'), __('this API key'));
             }elseif(!$errors['err']){
-                $errors['err']='Error updating API key. Try again!';
+                $errors['err']=sprintf(__('Error updating %s. Try again!'), __('this API key'));
             }
             break;
         case 'add':
             if(($id=API::add($_POST,$errors))){
-                $msg='API key added successfully';
+                $msg=sprintf(__('Successfully added %s'), __('an API key'));
                 $_REQUEST['a']=null;
             }elseif(!$errors['err']){
-                $errors['err']='Unable to add an API key. Correct error(s) below and try again.';
+                $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                    __('this API key'));
             }
             break;
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err'] = 'You must select at least one API key';
+                $errors['err'] = sprintf(__('You must select at least %s'), __('one API key'));
             } else {
                 $count=count($_POST['ids']);
                 switch(strtolower($_POST['a'])) {
@@ -50,11 +51,14 @@ if($_POST){
                             .' WHERE id IN ('.implode(',', db_input($_POST['ids'])).')';
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected API keys enabled';
+                                $msg = sprintf(__('Successfully enabled %s'),
+                                    _N('selected API key', 'selected API keys', $count));
                             else
-                                $warn = "$num of $count selected API keys enabled";
+                                $warn = sprintf(__('%1$d of %2$d %3$s enabled'), $num, $count,
+                                    _N('selected API key', 'selected API keys', $count));
                         } else {
-                            $errors['err'] = 'Unable to enable selected API keys.';
+                            $errors['err'] = sprintf(__('Unable to enable %s.'),
+                                _N('selected API key', 'selected API keys', $count));
                         }
                         break;
                     case 'disable':
@@ -62,11 +66,14 @@ if($_POST){
                             .' WHERE id IN ('.implode(',', db_input($_POST['ids'])).')';
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected API keys disabled';
+                                $msg = sprintf(__('Successfully disabled %s'),
+                                    _N('selected API key', 'selected API keys', $count));
                             else
-                                $warn = "$num of $count selected API keys disabled";
+                                $warn = sprintf(__('%1$d of %2$d %3$s disabled'), $num, $count,
+                                    _N('selected API key', 'selected API keys', $count));
                         } else {
-                            $errors['err']='Unable to disable selected API keys';
+                            $errors['err']=sprintf(__('Unable to disable %s'),
+                                _N('selected API key', 'selected API keys', $count));
                         }
                         break;
                     case 'delete':
@@ -76,19 +83,22 @@ if($_POST){
                                 $i++;
                         }
                         if($i && $i==$count)
-                            $msg = 'Selected API keys deleted successfully';
+                            $msg = sprintf(__('Successfully deleted %s'),
+                                _N('selected API key', 'selected API keys', $count));
                         elseif($i>0)
-                            $warn = "$i of $count selected API keys deleted";
+                            $warn = sprintf(__('%1$d of %2$d %3$s deleted'), $num, $count,
+                                _N('selected API key', 'selected API keys', $count));
                         elseif(!$errors['err'])
-                            $errors['err'] = 'Unable to delete selected API keys';
+                            $errors['err'] = sprintf(__('Unable to delete %s'),
+                                _N('selected API key', 'selected API keys', $count));
                         break;
                     default:
-                        $errors['err']='Unknown action - get technical help';
+                        $errors['err']=__('Unknown action - get technical help.');
                 }
             }
             break;
         default:
-            $errors['err']='Unknown action/command';
+            $errors['err']=__('Unknown action');
             break;
     }
 }
diff --git a/scp/attachment.php b/scp/attachment.php
index 28b9a185b4df488674d9157a13292cefb39bbc60..07f20981a19a982c3014033eab67465b9f5587a8 100644
--- a/scp/attachment.php
+++ b/scp/attachment.php
@@ -17,14 +17,14 @@ require('staff.inc.php');
 require_once(INCLUDE_DIR.'class.attachment.php');
 
 //Basic checks
-if(!$thisstaff || !$_GET['id'] || !$_GET['h'] 
-        || !($attachment=Attachment::lookup($_GET['id'])) 
+if(!$thisstaff || !$_GET['id'] || !$_GET['h']
+        || !($attachment=Attachment::lookup($_GET['id']))
         || !($file=$attachment->getFile()))
-    die('Unknown attachment!');
+    Http::response(404, __('Unknown or invalid file'));
 
 //Validate session access hash - we want to make sure the link is FRESH! and the user has access to the parent ticket!!
 $vhash=md5($attachment->getFileId().session_id().strtolower($file->getKey()));
-if(strcasecmp(trim($_GET['h']),$vhash) || !($ticket=$attachment->getTicket()) || !$ticket->checkStaffAccess($thisstaff)) die('Access Denied');
+if(strcasecmp(trim($_GET['h']),$vhash) || !($ticket=$attachment->getTicket()) || !$ticket->checkStaffAccess($thisstaff)) die(__('Access Denied'));
 
 //Download the file..
 $file->download();
diff --git a/scp/autocron.php b/scp/autocron.php
index 85e3cfbbb34c55f8085580daac7bd1fdd4e2c0b6..b6016399e0b4745c62891dd7cce4b3f77df6fcca 100644
--- a/scp/autocron.php
+++ b/scp/autocron.php
@@ -46,9 +46,12 @@ $thisstaff = null; //Clear staff obj to avoid false credit internal notes & auto
 Cron::TicketMonitor(); //Age tickets: We're going to age tickets regardless of cron settings.
 if($cfg && $cfg->isAutoCronEnabled()) { //ONLY fetch tickets if autocron is enabled!
     Cron::MailFetcher();  //Fetch mail.
-    $ost->logDebug('Auto Cron', 'Mail fetcher cron call ['.$caller.']');
+    $ost->logDebug(_S('Auto Cron'), sprintf(_S('Mail fetcher cron call [%s]'), $caller));
 }
 
+$data = array('autocron'=>true);
+Signal::send('cron', $data);
+
 $_SESSION['lastcroncall']=time();
 endif;
 ob_end_clean();
diff --git a/scp/banlist.php b/scp/banlist.php
index 4ef80f44d90d73c4dbc43b87f90061c2cfb63699..bbebfa4b7cc574884d2acd01f71e50dad19c1fdc 100644
--- a/scp/banlist.php
+++ b/scp/banlist.php
@@ -18,53 +18,55 @@ include_once(INCLUDE_DIR.'class.banlist.php');
 
 /* Get the system ban list filter */
 if(!($filter=Banlist::getFilter()))
-    $warn = 'System ban list is empty.';
+    $warn = __('System ban list is empty.');
 elseif(!$filter->isActive())
-    $warn = 'SYSTEM BAN LIST filter is <b>DISABLED</b> - <a href="filters.php">enable here</a>.';
+    // XXX: This should never happen and can no longer be enabled via
+    // this link
+    $warn = __('SYSTEM BAN LIST filter is <b>DISABLED</b>').' - <a href="filters.php">'.__('enable here').'</a>.';
 
 $rule=null; //ban rule obj.
 if($filter && $_REQUEST['id'] && !($rule=$filter->getRule($_REQUEST['id'])))
-    $errors['err'] = 'Unknown or invalid ban list ID #';
+    $errors['err'] = sprintf(__('%s: Unknown or invalid ID.'), __('ban list'));
 
 if($_POST && !$errors && $filter){
     switch(strtolower($_POST['do'])){
         case 'update':
             if(!$rule){
-                $errors['err']='Unknown or invalid ban rule.';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('ban rule'));
             }elseif(!$_POST['val'] || !Validator::is_email($_POST['val'])){
-                $errors['err']=$errors['val']='Valid email address required';
+                $errors['err']=$errors['val']=__('Valid email address required');
             }elseif(!$errors){
-                $vars=array('w'=>'email',
-                            'h'=>'equal',
-                            'v'=>trim($_POST['val']),
+                $vars=array('what'=>'email',
+                            'how'=>'equal',
+                            'val'=>trim($_POST['val']),
                             'filter_id'=>$filter->getId(),
                             'isactive'=>$_POST['isactive'],
                             'notes'=>$_POST['notes']);
                 if($rule->update($vars,$errors)){
-                    $msg='Email updated successfully';
+                    $msg=sprintf(__('Successfully updated %s'), Format::htmlchars($_POST['val']));
                 }elseif(!$errors['err']){
-                    $errors['err']='Error updating ban rule. Try again!';
+                    $errors['err']=sprintf(__('Error updating %s. Try again!'), __('this ban rule'));
                 }
             }
             break;
         case 'add':
             if(!$filter) {
-                $errors['err']='Unknown or invalid ban list';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('ban list'));
             }elseif(!$_POST['val'] || !Validator::is_email($_POST['val'])) {
-                $errors['err']=$errors['val']='Valid email address required';
+                $errors['err']=$errors['val']=__('Valid email address required');
             }elseif(BanList::includes(trim($_POST['val']))) {
-                $errors['err']=$errors['val']='Email already in the ban list';
+                $errors['err']=$errors['val']=__('Email already in the ban list');
             }elseif($filter->addRule('email','equal',trim($_POST['val']),array('isactive'=>$_POST['isactive'],'notes'=>$_POST['notes']))) {
-                $msg='Email address added to ban list successfully';
+                $msg=__('Email address added to ban list successfully');
                 $_REQUEST['a']=null;
                 //Add filter rule here.
             }elseif(!$errors['err']){
-                $errors['err']='Error creating ban rule. Try again!';
+                $errors['err']=sprintf(__('Error creating %s. Try again!'), __('ban rule'));
             }
             break;
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err'] = 'You must select at least one email to process.';
+                $errors['err'] = __('You must select at least one email to process.');
             } else {
                 $count=count($_POST['ids']);
                 switch(strtolower($_POST['a'])) {
@@ -74,11 +76,14 @@ if($_POST && !$errors && $filter){
                             .' AND id IN ('.implode(',', db_input($_POST['ids'])).')';
                         if(db_query($sql) && ($num=db_affected_rows())){
                             if($num==$count)
-                                $msg = 'Selected emails ban status set to enabled';
+                                $msg = sprintf(__('Successfully enabled %s'),
+                                    _N('selected ban rule', 'selected ban rules', $count));
                             else
-                                $warn = "$num of $count selected emails ban status enabled";
+                                $warn = sprintf(__('%1$d of %2$d %3$s enabled'), $num, $count,
+                                    _N('selected ban rule', 'selected ban rules', $count));
                         } else  {
-                            $errors['err'] = 'Unable to enable selected emails';
+                            $errors['err'] = sprintf(__('Unable to enable %s'),
+                                _N('selected ban rule', 'selected ban rules', $count));
                         }
                         break;
                     case 'disable':
@@ -87,11 +92,14 @@ if($_POST && !$errors && $filter){
                             .' AND id IN ('.implode(',', db_input($_POST['ids'])).')';
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected emails ban status set to disabled';
+                                $msg = sprintf(__('Successfully disabled %s'),
+                                    _N('selected ban rule', 'selected ban rules', $count));
                             else
-                                $warn = "$num of $count selected emails ban status set to disabled";
+                                $warn = sprintf(__('%1$d of %2$d %3$s disabled'), $num, $count,
+                                    _N('selected ban rule', 'selected ban rules', $count));
                         } else {
-                            $errors['err'] = 'Unable to disable selected emails';
+                            $errors['err'] = sprintf(__('Unable to disable %s'),
+                                _N('selected ban rule', 'selected ban rules', $count));
                         }
                         break;
                     case 'delete':
@@ -101,20 +109,23 @@ if($_POST && !$errors && $filter){
                                 $i++;
                         }
                         if($i && $i==$count)
-                            $msg = 'Selected emails deleted from banlist successfully';
+                            $msg = sprintf(__('Successfully deleted %s'),
+                                _N('selected ban rule', 'selected ban rules', $count));
                         elseif($i>0)
-                            $warn = "$i of $count selected emails deleted from banlist";
+                            $warn = sprintf(__('%1$d of %2$d %3$s deleted'), $i, $count,
+                                _N('selected ban rule', 'selected ban rules', $count));
                         elseif(!$errors['err'])
-                            $errors['err'] = 'Unable to delete selected emails';
+                            $errors['err'] = sprintf(__('Unable to delete %s'),
+                                _N('selected ban rule', 'selected ban rules', $count));
 
                         break;
                     default:
-                        $errors['err'] = 'Unknown action - get technical help';
+                        $errors['err'] = __('Unknown action - get technical help.');
                 }
             }
             break;
         default:
-            $errors['err']='Unknown action';
+            $errors['err']=__('Unknown action');
             break;
     }
 }
diff --git a/scp/canned.php b/scp/canned.php
index ef9fc922f1fee99c536608b5d79aeeb57f8dfea3..5de2f87b50e128d08c615275f9c6e2e25b5d14e8 100644
--- a/scp/canned.php
+++ b/scp/canned.php
@@ -15,6 +15,7 @@
 **********************************************************************/
 require('staff.inc.php');
 include_once(INCLUDE_DIR.'class.canned.php');
+
 /* check permission */
 if(!$thisstaff || !$thisstaff->canManageCannedResponses()) {
     header('Location: kb.php');
@@ -25,27 +26,36 @@ if(!$thisstaff || !$thisstaff->canManageCannedResponses()) {
 
 $canned=null;
 if($_REQUEST['id'] && !($canned=Canned::lookup($_REQUEST['id'])))
-    $errors['err']='Unknown or invalid canned response ID.';
+    $errors['err']=sprintf(__('%s: Unknown or invalid ID.'), __('canned response'));
+
+$canned_form = new Form(array(
+    'attachments' => new FileUploadField(array('id'=>'attach',
+        'configuration'=>array('extensions'=>false,
+            'size'=>$cfg->getMaxFileSize())
+   )),
+));
 
 if($_POST && $thisstaff->canManageCannedResponses()) {
     switch(strtolower($_POST['do'])) {
         case 'update':
             if(!$canned) {
-                $errors['err']='Unknown or invalid canned response.';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('canned response'));
             } elseif($canned->update($_POST, $errors)) {
-                $msg='Canned response updated successfully';
+                $msg=sprintf(__('Successfully updated %s'),
+                    __('this canned response'));
                 //Delete removed attachments.
                 //XXX: files[] shouldn't be changed under any circumstances.
-                $keepers = $_POST['files']?$_POST['files']:array();
+                $keepers = $canned_form->getField('attachments')->getClean();
                 $attachments = $canned->attachments->getSeparates(); //current list of attachments.
                 foreach($attachments as $k=>$file) {
                     if($file['id'] && !in_array($file['id'], $keepers)) {
                         $canned->attachments->delete($file['id']);
                     }
                 }
+
                 //Upload NEW attachments IF ANY - TODO: validate attachment types??
-                if($_FILES['attachments'] && ($files=AttachmentFile::format($_FILES['attachments'])))
-                    $canned->attachments->upload($files);
+                if ($keepers)
+                    $canned->attachments->upload($keepers);
 
                 // Attach inline attachments from the editor
                 if (isset($_POST['draft_id'])
@@ -67,19 +77,20 @@ if($_POST && $thisstaff->canManageCannedResponses()) {
                 // Delete drafts for all users for this canned response
                 Draft::deleteForNamespace('canned.'.$canned->getId());
             } elseif(!$errors['err']) {
-                $errors['err']='Error updating canned response. Try again!';
+                $errors['err']=sprintf(__('Error updating %s. Try again!'), __('this canned response'));
             }
             break;
         case 'create':
             if(($id=Canned::create($_POST, $errors))) {
-                $msg='Canned response added successfully';
+                $msg=sprintf(__('Successfully added %s'), Format::htmlchars($_POST['title']));
                 $_REQUEST['a']=null;
                 //Upload attachments
-                if($_FILES['attachments'] && ($c=Canned::lookup($id)) && ($files=AttachmentFile::format($_FILES['attachments'])))
-                    $c->attachments->upload($files);
+                $keepers = $canned_form->getField('attachments')->getClean();
+                if (($c=Canned::lookup($id)) && $keepers)
+                    $c->attachments->upload($keepers);
 
                 // Attach inline attachments from the editor
-                if (isset($_POST['draft_id'])
+                if ($c && isset($_POST['draft_id'])
                         && ($draft = Draft::lookup($_POST['draft_id'])))
                     $c->attachments->upload(
                         $draft->getAttachmentIds($_POST['response']), true);
@@ -87,12 +98,13 @@ if($_POST && $thisstaff->canManageCannedResponses()) {
                 // Delete this user's drafts for new canned-responses
                 Draft::deleteForNamespace('canned', $thisstaff->getId());
             } elseif(!$errors['err']) {
-                $errors['err']='Unable to add canned response. Correct error(s) below and try again.';
+                $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                    __('this canned response'));
             }
             break;
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err']='You must select at least one canned response';
+                $errors['err']=sprintf(__('You must select at least %s'), __('one canned response'));
             } else {
                 $count=count($_POST['ids']);
                 switch(strtolower($_POST['a'])) {
@@ -101,11 +113,14 @@ if($_POST && $thisstaff->canManageCannedResponses()) {
                             .' WHERE canned_id IN ('.implode(',', db_input($_POST['ids'])).')';
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected canned responses enabled';
+                                $msg = sprintf(__('Successfully enabled %s'),
+                                    _N('selected canned response', 'selected canned responses', $count));
                             else
-                                $warn = "$num of $count selected canned responses enabled";
+                                $warn = sprintf(__('%1$d of %2$d %s enabled'), $num, $count,
+                                    _N('selected canned response', 'selected canned responses', $count));
                         } else {
-                            $errors['err'] = 'Unable to enable selected canned responses.';
+                            $errors['err'] = sprintf(__('Unable to enable %s.'),
+                                _N('selected canned response', 'selected canned responses', $count));
                         }
                         break;
                     case 'disable':
@@ -113,11 +128,14 @@ if($_POST && $thisstaff->canManageCannedResponses()) {
                             .' WHERE canned_id IN ('.implode(',', db_input($_POST['ids'])).')';
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected canned responses disabled';
+                                $msg = sprintf(__('Successfully disabled %s'),
+                                    _N('selected canned response', 'selected canned responses', $count));
                             else
-                                $warn = "$num of $count selected canned responses disabled";
+                                $warn = sprintf(__('%1$d of %2$d %s disabled'), $num, $count,
+                                    _N('selected canned response', 'selected canned responses', $count));
                         } else {
-                            $errors['err'] = 'Unable to disable selected canned responses';
+                            $errors['err'] = sprintf(__('Unable to disable %s'),
+                                _N('selected canned response', 'selected canned responses', $count));
                         }
                         break;
                     case 'delete':
@@ -129,19 +147,22 @@ if($_POST && $thisstaff->canManageCannedResponses()) {
                         }
 
                         if($i==$count)
-                            $msg = 'Selected canned responses deleted successfully';
+                            $msg = sprintf(__('Successfully deleted %s'),
+                                _N('selected canned response', 'selected canned responses', $count));
                         elseif($i>0)
-                            $warn="$i of $count selected canned responses deleted";
+                            $warn=sprintf(__('%1$d of %2$d %3$s deleted'), $i, $count,
+                                _N('selected canned response', 'selected canned responses', $count));
                         elseif(!$errors['err'])
-                            $errors['err'] = 'Unable to delete selected canned responses';
+                            $errors['err'] = sprintf(__('Unable to delete %s'),
+                                _N('selected canned response', 'selected canned responses', $count));
                         break;
                     default:
-                        $errors['err']='Unknown command';
+                        $errors['err']=__('Unknown command');
                 }
             }
             break;
         default:
-            $errors['err']='Unknown action';
+            $errors['err']=__('Unknown action');
             break;
     }
 }
@@ -157,5 +178,6 @@ $ost->addExtraHeader('<meta name="tip-namespace" content="' . $tip_namespace . '
     "$('#content').data('tipNamespace', '".$tip_namespace."');");
 require(STAFFINC_DIR.'header.inc.php');
 require(STAFFINC_DIR.$page);
+print $canned_form->getMedia();
 include(STAFFINC_DIR.'footer.inc.php');
 ?>
diff --git a/scp/categories.php b/scp/categories.php
index d707c864bc3f3e577f484bdd7691c0a78ea34c5d..8afdc4c0f47da5ee7d9a52341fe509a9718bea71 100644
--- a/scp/categories.php
+++ b/scp/categories.php
@@ -25,30 +25,32 @@ if(!$thisstaff || !$thisstaff->canManageFAQ()) {
 
 $category=null;
 if($_REQUEST['id'] && !($category=Category::lookup($_REQUEST['id'])))
-    $errors['err']='Unknown or invalid category ID.';
+    $errors['err']=sprintf(__('%s: Unknown or invalid ID.'), __('category'));
 
 if($_POST){
     switch(strtolower($_POST['do'])) {
         case 'update':
             if(!$category) {
-                $errors['err']='Unknown or invalid category.';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('category'));
             } elseif($category->update($_POST,$errors)) {
-                $msg='Category updated successfully';
+                $msg=sprintf(__('Successfully updated %s'),
+                    __('this category'));
             } elseif(!$errors['err']) {
-                $errors['err']='Error updating category. Try again!';
+                $errors['err']=sprintf(__('Error updating %s. Correct error(s) below and try again.'), __('this category'));
             }
             break;
         case 'create':
             if(($id=Category::create($_POST,$errors))) {
-                $msg='Category added successfully';
+                $msg=sprintf(__('Successfull added %s'), Format::htmlchars($_POST['name']));
                 $_REQUEST['a']=null;
             } elseif(!$errors['err']) {
-                $errors['err']='Unable to add category. Correct error(s) below and try again.';
+                $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                    __('this category'));
             }
             break;
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err']='You must select at least one category';
+                $errors['err']=sprintf(__('You must select at least %s'), __('one category'));
             } else {
                 $count=count($_POST['ids']);
                 switch(strtolower($_POST['a'])) {
@@ -58,11 +60,14 @@ if($_POST){
 
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected categories made PUBLIC';
+                                $msg = sprintf(__('Successfully made %s PUBLIC'),
+                                    _N('selected category', 'selected categories', $count));
                             else
-                                $warn = "$num of $count selected categories made PUBLIC";
+                                $warn = sprintf(__('%1$d of %2$d %3$s made PUBLIC'), $num, $count,
+                                    _N('selected category', 'selected categories', $count));
                         } else {
-                            $errors['err'] = 'Unable to enable selected categories public.';
+                            $errors['err'] = sprintf(__('Unable to make %s PUBLIC.'),
+                                _N('selected category', 'selected categories', $count));
                         }
                         break;
                     case 'make_private':
@@ -71,11 +76,14 @@ if($_POST){
 
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected categories made PRIVATE';
+                                $msg = sprintf(__('Successfully made %s PRIVATE'),
+                                    _N('selected category', 'selected categories', $count));
                             else
-                                $warn = "$num of $count selected categories made PRIVATE";
+                                $warn = sprintf(__('%1$d of %2$d %3$s made PRIVATE'), $num, $count,
+                                    _N('selected category', 'selected categories', $count));
                         } else {
-                            $errors['err'] = 'Unable to disable selected categories PRIVATE';
+                            $errors['err'] = sprintf(__('Unable to make %s PRIVATE'),
+                                _N('selected category', 'selected categories', $count));
                         }
                         break;
                     case 'delete':
@@ -86,19 +94,22 @@ if($_POST){
                         }
 
                         if($i==$count)
-                            $msg = 'Selected categories deleted successfully';
+                            $msg = sprintf(__('Successfully deleted %s'),
+                                _N('selected category', 'selected categories', $count));
                         elseif($i>0)
-                            $warn = "$i of $count selected categories deleted";
+                            $warn = sprintf(__('%1$d of %2$d %3$s deleted'), $i, $count,
+                                _N('selected category', 'selected categories', $count));
                         elseif(!$errors['err'])
-                            $errors['err'] = 'Unable to delete selected categories';
+                            $errors['err'] = sprintf(__('Unable to delete %s'),
+                                _N('selected category', 'selected categories', $count));
                         break;
                     default:
-                        $errors['err']='Unknown action/command';
+                        $errors['err']=__('Unknown action - get technical help.');
                 }
             }
             break;
         default:
-            $errors['err']='Unknown action';
+            $errors['err']=__('Unknown action');
             break;
     }
 }
diff --git a/scp/css/bootstrap.css b/scp/css/bootstrap.css
index 596bdd56453bc4187ae7e1368c80d7f9918fb652..7a02fb748814898548e67f60e34185991712de67 100644
--- a/scp/css/bootstrap.css
+++ b/scp/css/bootstrap.css
@@ -1268,7 +1268,7 @@ legend + .control-group {
 }
 .nav-tabs > li,
 .nav-pills > li {
-  float: left;
+  display: inline-block;
 }
 .nav-tabs > li > a,
 .nav-pills > li > a {
diff --git a/scp/css/dropdown.css b/scp/css/dropdown.css
index 961abd6fa0fb3b7945ef6bcd56594c1c947ddb8d..4fb664178aaa2ab61f663d6b9219ad00740bf64d 100644
--- a/scp/css/dropdown.css
+++ b/scp/css/dropdown.css
@@ -104,7 +104,6 @@
   padding: 0 5px;
   text-decoration: none !important;
   line-height:18px;
-  float:right;
   margin-left:5px;
 }
 .action-button span,
@@ -121,7 +120,6 @@
   background-image: -ms-linear-gradient(top, #efefef 0%, #dddddd 100%);
   background-image: -o-linear-gradient(top, #efefef 0%, #dddddd 100%);
   background-image: linear-gradient(top, #efefef 0%, #dddddd 100%);
-  float: right;
   height: 18px;
   line-height: 18px;
   margin-right: 0;
diff --git a/scp/css/scp.css b/scp/css/scp.css
index e99d9c36bce30f4c7e0b57f83e410dc0efc2ddf8..2fe63818d17da1f6514482d6b93a69b27c96791b 100644
--- a/scp/css/scp.css
+++ b/scp/css/scp.css
@@ -10,6 +10,7 @@ body {
 a {
     color:#184E81;
     text-decoration:none;
+    display: inline-block;
 }
 
 a:hover {
@@ -99,7 +100,6 @@ div#header a {
 
 #logo {
     display:block;
-    float:left;
     width:190px;
     height:76px;
     text-decoration:none;
@@ -110,8 +110,8 @@ div#header a {
 
 #header p {
     display:block;
-    width:430px;
-    float:right;
+    width:auto;
+    max-width:630px;
     margin:10px;
     background:#eee;
     border:1px solid #ccc;
@@ -130,7 +130,6 @@ div#header a {
 }
 
 #nav .active, #sub_nav li {
-    margin:0;
     padding:0;
     list-style:none;
     display:inline;
@@ -144,17 +143,22 @@ div#header a {
     border-bottom:1px solid #c5d9ec;
 }
 
-#nav .active a, #nav .inactive {
-    display:block;
-    float:left;
-    width:115px;
+#nav .active, #nav .inactive {
+    display:inline-block;
+    min-width:95px;
+    width: auto;
+    padding-left: 10px;
+    padding-right: 10px;
     height:26px;
     color:#555;
     text-align:center;
     font-weight:bold;
-    margin-top:1px;
-    margin-right:5px;
     position:relative;
+
+    border-radius:5px 5px 0 0;
+    border-style: solid;
+    border-width: 1px 1px 0;
+    border-color: transparent;
 }
 
 #nav .inactive a {
@@ -163,21 +167,47 @@ div#header a {
 }
 
 #nav .active a {
-    background:url(../images/tab-bg.png) top left no-repeat;
     color:#004a80;
 }
 
-#nav .inactive ul {
-    display:none;
+#nav > li + li {
+    margin-left: 8px;
+}
+
+#nav li.active, #nav li.inactive:hover {
+    box-shadow: 4px -3px 6px -3px rgba(0,0,0,0.3);
+    border-color: #c5d9ec;
+}
+#nav li.active {
+    background-color: #f7f7f7;
+}
+#nav li.inactive:hover {
+    background-color: #fbfbfb;
+}
+
+#nav li.inactive > ul {
     width:230px;
     background:#fbfbfb;
     margin:0;
     padding:0;
-    position:relative;
+    position:absolute;
+    left: -1px;
     z-index:500;
     border-bottom:1px solid #ccc;
     border-left:1px solid #ccc;
     border-right:1px solid #ccc;
+    border-radius: 0 0 5px 5px;
+
+    display:block;
+    padding-left: 5px;
+    -moz-box-shadow: 3px 3px 3px #ccc;
+    -webkit-box-shadow: 3px 3px 3px #ccc;
+    box-shadow: 3px 3px 3px #ccc;
+    box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
+
+    visibility: hidden;
+    opacity: 0;
+    transition: visibility 0s linear, opacity 0.1s linear;
 }
 
 #nav .inactive li {
@@ -188,15 +218,10 @@ div#header a {
     text-align:left;
 }
 
-#nav .inactive:hover {
-    background:url(../images/tab-bg.png) bottom left no-repeat;
-}
-
-#nav .inactive:hover ul {
-    display:block;
-    -moz-box-shadow: 3px 3px 3px #ccc;
-    -webkit-box-shadow: 3px 3px 3px #ccc;
-    box-shadow: 3px 3px 3px #ccc;
+#nav li.inactive:hover > ul {
+    visibility: visible;
+    opacity: 1;
+    transition-delay: 0.25s;
 }
 
 .ieshadow {
@@ -211,16 +236,11 @@ div#header a {
     left:0;
 }
 
-#nav .inactive li {
-    background:#fbfbfb;
-}
-
 #nav .inactive li a {
     padding-left:24px;
     background-position:0 50%;
     background-repeat:no-repeat;
     font-weight:normal;
-    background-color:#fbfbfb;
 }
 
 #nav .inactive li a:hover {
@@ -233,14 +253,15 @@ div#header a {
 }
 
 #sub_nav a {
-    display:block;
-    float:left;
-    margin-right:10px;
+    display:inline-block;
     padding:0 10px 0 21px;
     background-position:0 50%;
     background-repeat:no-repeat;
     color:#000;
 }
+#sub_nav li + li > a {
+    margin-left:10px;
+}
 
 #sub_nav a:hover {
     color:#E65524;
@@ -434,6 +455,7 @@ table.list thead th {
     color:#000;
     text-align:left;
     vertical-align:top;
+    padding: 0 4px;
 }
 
 table.list th a {
@@ -442,7 +464,7 @@ table.list th a {
     color:#000;
 }
 
-table.list thead th a { padding: 3px; display: block; color: #000; background: url('../images/asc_desc.gif') 100% 50% no-repeat; }
+table.list thead th a { padding: 3px; padding-right: 15px; display: block; white-space: nowrap; color: #000; background: url('../images/asc_desc.gif') 100% 50% no-repeat; }
 
 table.list thead th a.asc { background: url('../images/asc.gif') 100% 50% no-repeat #cfe6ff; }
 table.list thead th a.desc { background: url('../images/desc.gif') 100% 50% no-repeat #cfe6ff; }
@@ -453,7 +475,7 @@ table.list tbody td {
     vertical-align:top;
 }
 
-table.list tbody td { background: #fff; padding: 1px; padding-left:2px; vertical-align: top; }
+table.list tbody td { background: #fff; padding: 1px 3px; vertical-align: top; }
 table.list tbody tr:nth-child(2n+1) td { background-color: #f0faff; }
 table.list tbody tr:hover td { background: #ffe; }
 table.list tbody tr:nth-child(2n+1):hover td { background: #ffd; }
@@ -760,9 +782,8 @@ h2 .reload {
 }
 
 #threads li a {
-    display:block;
+    display:inline-block;
     width:auto;
-    float:left;
     height:30px;
     line-height:30px;
     border-top:1px solid #F4FAFF;
@@ -872,7 +893,7 @@ ul.tabs {
     padding:4px 0 0 20px;
     margin:0;
     margin-bottom: 5px;
-    text-align:center;
+    text-align:left;
     height:29px;
     border-bottom:1px solid #aaa;
     background:#eef3f8;
@@ -891,20 +912,20 @@ ul.tabs li {
 }
 
 ul.tabs li a {
-    width:130px;
+    min-width:130px;
     font-weight:bold;
     padding:5px;
     height:18px;
     line-height:20px;
     color:#444;
-    display:block;
-    float:left;
+    display:inline-block;
     outline:none;
     position:relative;
-    top:0;
+    bottom:1px;
     background:#fbfbfb;
     border:1px solid #eee;
     border-bottom:none;
+    text-align: center;
 }
 
 #response_options .reply_tab.tell {
@@ -921,6 +942,7 @@ ul.tabs li a.active {
     border:1px solid #aaa;
     border-top:2px solid #81a9d7;
     border-bottom:none;
+    bottom: 0;
 }
 
 #response_options > form {
@@ -1312,9 +1334,21 @@ time {
     box-shadow: 0 5px 60px #001;
     border-radius: 5px;
     max-height: 72%;
+    min-height: 50px;
     overflow-y: auto;
 }
 
+.dialog #popup-loading {
+    position:absolute;
+    text-align:center;
+    background:rgba(255,255,255,0.8);
+    top:0;
+    bottom:0;
+    left:0;
+    right:0;
+    z-index:1;
+}
+
 .redactor_editor {
     font-size: 11pt;
 }
@@ -1375,7 +1409,14 @@ time {
     overflow:hidden;
 }
 
-.dialog label {
+.dialog .custom-field .field-label {
+    margin-left: 3px;
+    margin-right: 3px;
+}
+.dialog .custom-field + .custom-field {
+    margin-top: 8px;
+}
+.dialog label.fixed-size {
     width:100px;
     display:inline-block;
     text-align:right;
@@ -1387,12 +1428,7 @@ time {
     background:#fff;
 }
 
-.dialog fieldset select {
-    width:170px;
-    display:inline-block;
-}
-
-.dialog fieldset span {
+.dialog fieldset span.between {
     width:50px;
     display:inline-block;
     text-align:center;
@@ -1407,15 +1443,35 @@ time {
 }
 
 .dialog.draggable h3:hover {
-    cursor: crosshair;
+    cursor: move;
+}
+
+#advanced-search fieldset.span6 {
+    display: inline-block;
+    width: 49%;
+    margin-bottom: 5px;
+}
+#advanced-search fieldset label {
+    display: block;
+}
+#advanced-search fieldset.span6 select,
+#advanced-search fieldset.span6 input {
+    max-width: 100%;
+    min-width: 75%;
 }
 
 #advanced-search .query input {
-    width:350px;
+    width:100%;
+    padding: 4px;
+    margin-bottom: 10px;
 }
 
+#advanced-search .date_range {
+    margin-bottom: 5px;
+}
 #advanced-search .date_range input {
-    width:175px;
+    width:227px;
+    width: calc(49% - 73px);
 }
 
 #advanced-search .date_range i {
@@ -1600,12 +1656,13 @@ div.patch {
 }
 
 div.selected-signature {
-    border-top: 1px dotted #aaa;
+    border: 1px solid #ddd;
+    border: 1px solid rgba(0,0,0,0.1);
+    border-top-style: dotted;
+    border-bottom-style: none;
     padding: 0.3em 10px 5px;
-    background-color: #f9f9f9;
     height: 2.5em;
     overflow-y: hidden;
-    box-shadow: inset 0px -5px 5px -5px rgba(206, 199, 182, 1.0);
     font-size: 15px;
     line-height: 1.25rem;
 }
@@ -1646,46 +1703,68 @@ div.selected-signature .inner {
     cursor: move;
 }
 
-.sortable-row-item {
+.sortable {
+    cursor: move;
+}
+.row-item {
     border: 1px solid rgba(0, 0, 0, 0.7);
     padding: 9px;
-    cursor: move;
     position: relative;
 }
-.sortable-row-item:hover {
+.sortable:hover {
     background: rgba(0, 0, 0, 0.1);
 }
-.sortable-row-item:active {
+.sortable:active {
     background: rgba(0, 0, 0, 0.3);
 }
-.sortable-row-item:first-child {
+.row-item:first-child {
     border-top-right-radius: 5px;
     border-top-left-radius: 5px;
 }
-.sortable-row-item:last-child {
+.row-item:last-child {
     border-bottom-right-radius: 5px;
     border-bottom-left-radius: 5px;
 }
-.sortable-row-item + .sortable-row-item {
+.row-item + .row-item {
     margin-top: -1px;
 }
 
-.sortable-row-item .delete {
+.row-item .delete {
     border-left: 1px solid rgba(0, 0, 0, 0.7);
     border-top-right-radius: inherit;
     border-bottom-right-radius: inherit;
+    width: 35px;
+}
+.row-item .delete:empty {
+    visibility: hidden;
+}
+
+.row-item .button-group {
+    font-size: 105%;
     position: absolute;
     top: 0px;
     right: 0;
+    display: inline-block;
+}
+
+.row-item .button-group div {
     padding: 9px;
     padding-left: 12px;
-    font-size: 105%;
+    display: inline-block;
+}
+.row-item .management {
+    margin-top: 10px;
+    border-top: 1px dashed black;
 }
 
-.sortable-row-item .delete:hover {
+.row-item .delete:hover {
     background: #fc9f41; /* Old browsers */
     color: rgba(255,255,255,0.8) !important;
 }
+#sequences .manage-buttons {
+    display: inline-block;
+    margin-right: 60px;
+}
 
 tr.disabled td,
 tr.disabled th {
@@ -1773,6 +1852,20 @@ tr.disabled th {
 .label-info {
   background-color: #3a87ad;
 }
+.label-verified {
+    border:1px solid green;
+    background-color:transparent;
+    background-color:rgba(0,0,0,0);
+    color:green;
+    text-shadow:none;
+}
+.label-danger {
+    border:1px solid red;
+    background-color:transparent;
+    background-color:rgba(0,0,0,0);
+    color:red;
+    text-shadow:none;
+}
 
 .tab_content {
     position: relative;
@@ -1834,8 +1927,32 @@ table.custom-info td {
     border-radius: 3px;
     text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
     line-height: 14px;
+    position: absolute;
+    top: 3em;
+    right: 0.5em;
 }
 
 .delete-draft:hover {
     background-color: #fc9f41 !important;
 }
+
+.hidden {
+    display: none;
+}
+
+.pull-right {
+    float: right;
+}
+.flush-right {
+    text-align: right;
+}
+.flush-left {
+    text-align: left;
+}
+.ltr {
+    direction: ltr;
+    unicode-bidi: embed;
+}
+.required {
+    font-weight: bold;
+}
diff --git a/scp/dashboard.php b/scp/dashboard.php
index ab930683e132fdd64befdbfa90cf237217470c7a..8e48056787a4c86402040162f362f86a440cda95 100644
--- a/scp/dashboard.php
+++ b/scp/dashboard.php
@@ -30,25 +30,27 @@ require(STAFFINC_DIR.'header.inc.php');
 <link rel="stylesheet" type="text/css" href="css/bootstrap.css"/>
 <link rel="stylesheet" type="text/css" href="css/dashboard.css"/>
 
-<h2>Ticket Activity&nbsp;<i class="help-tip icon-question-sign" href="#ticket_activity"></i></h2>
-<p>Select the starting time and period for the system activity graph</p>
+<h2><?php echo __('Ticket Activity');
+?>&nbsp;<i class="help-tip icon-question-sign" href="#ticket_activity"></i></h2>
+<p><?php echo __('Select the starting time and period for the system activity graph');?></p>
 <form class="well form-inline" id="timeframe-form">
     <label>
-        <i class="help-tip icon-question-sign" href="#report_timeframe"></i>&nbsp;&nbsp;Report timeframe:
+        <i class="help-tip icon-question-sign" href="#report_timeframe"></i>&nbsp;&nbsp;<?php
+            echo __('Report timeframe'); ?>:
         <input type="text" class="dp input-medium search-query"
-            name="start" placeholder="Last month"/>
+            name="start" placeholder="<?php echo __('Last month');?>"/>
     </label>
     <label>
-        period:
+        <?php echo __('period');?>:
         <select name="period">
-            <option value="now" selected="selected">Up to today</option>
-            <option value="+7 days">One Week</option>
-            <option value="+14 days">Two Weeks</option>
-            <option value="+1 month">One Month</option>
-            <option value="+3 months">One Quarter</option>
+            <option value="now" selected="selected"><?php echo __('Up to today');?></option>
+            <option value="+7 days"><?php echo __('One Week');?></option>
+            <option value="+14 days"><?php echo __('Two Weeks');?></option>
+            <option value="+1 month"><?php echo __('One Month');?></option>
+            <option value="+3 months"><?php echo __('One Quarter');?></option>
         </select>
     </label>
-    <button class="btn" type="submit">Refresh</button>
+    <button class="btn" type="submit"><?php echo __('Refresh');?></button>
 </form>
 
 <!-- Create a graph and fetch some data to create pretty dashboard -->
@@ -58,8 +60,8 @@ require(STAFFINC_DIR.'header.inc.php');
 </div>
 
 <hr/>
-<h2>Statistics&nbsp;<i class="help-tip icon-question-sign" href="#statistics"></i></h2>
-<p>Statistics of tickets organized by department, help topic, and staff.</p>
+<h2><?php echo __('Statistics'); ?>&nbsp;<i class="help-tip icon-question-sign" href="#statistics"></i></h2>
+<p><?php echo __('Statistics of tickets organized by department, help topic, and agent.');?></p>
 <ul class="nav nav-tabs" id="tabular-navigation"></ul>
 
 <div id="table-here"></div>
diff --git a/scp/departments.php b/scp/departments.php
index abb9574902a050c4aa12d3b5e1bc74e8647b281f..999380219ca49a9293089fb51a6a7272d6af93d5 100644
--- a/scp/departments.php
+++ b/scp/departments.php
@@ -14,34 +14,39 @@
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
 require('admin.inc.php');
+
 $dept=null;
 if($_REQUEST['id'] && !($dept=Dept::lookup($_REQUEST['id'])))
-    $errors['err']='Unknown or invalid department ID.';
+    $errors['err']=sprintf(__('%s: Unknown or invalid ID.'), __('department'));
 
 if($_POST){
     switch(strtolower($_POST['do'])){
         case 'update':
             if(!$dept){
-                $errors['err']='Unknown or invalid department.';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('department'));
             }elseif($dept->update($_POST,$errors)){
-                $msg='Department updated successfully';
+                $msg=sprintf(__('Successfully updated %s'),
+                    __('this department'));
             }elseif(!$errors['err']){
-                $errors['err']='Error updating department. Try again!';
+                $errors['err']=sprintf(__('Error updating %s. Try again!'),
+                    __('this department'));
             }
             break;
         case 'create':
             if(($id=Dept::create($_POST,$errors))){
-                $msg=Format::htmlchars($_POST['name']).' added successfully';
+                $msg=sprintf(__('Successfully added "%s"'),Format::htmlchars($_POST['name']));
                 $_REQUEST['a']=null;
             }elseif(!$errors['err']){
-                $errors['err']='Unable to add department. Correct error(s) below and try again.';
+                $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                    __('this department'));
             }
             break;
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err'] = 'You must select at least one department';
+                $errors['err'] = sprintf(__('You must select at least %s'),
+                    __('one department'));
             }elseif(in_array($cfg->getDefaultDeptId(),$_POST['ids'])) {
-                $errors['err'] = 'You can not disable/delete a default department. Remove default Dept. and try again.';
+                $errors['err'] = __('You cannot disable/delete a default department. Select a new default department and try again.');
             }else{
                 $count=count($_POST['ids']);
                 switch(strtolower($_POST['a'])) {
@@ -50,11 +55,17 @@ if($_POST){
                             .' WHERE dept_id IN ('.implode(',', db_input($_POST['ids'])).')';
                         if(db_query($sql) && ($num=db_affected_rows())){
                             if($num==$count)
-                                $msg='Selected departments made public';
+                                $msg=sprintf(__('Successfully made %s PUBLIC'),
+                                    _N('selected department', 'selected departments', $count));
                             else
-                                $warn="$num of $count selected departments made public";
+                                $warn=sprintf(__(
+                                    /* Phrase will read:
+                                       <a> of <b> <selected objects> made PUBLIC */
+                                    '%1$d of %2$d %s made PUBLIC'), $num, $count,
+                                    _N('selected department', 'selected departments', $count));
                         } else {
-                            $errors['err']='Unable to make selected department public.';
+                            $errors['err']=sprintf(__('Unable to make %s PUBLIC.'),
+                                _N('selected department', 'selected departments', $count));
                         }
                         break;
                     case 'make_private':
@@ -63,11 +74,17 @@ if($_POST){
                             .' AND dept_id!='.db_input($cfg->getDefaultDeptId());
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected departments made private';
+                                $msg = sprintf(__('Successfully made %s PRIVATE'),
+                                    _N('selected department', 'selected epartments', $count));
                             else
-                                $warn = "$num of $count selected departments made private";
+                                $warn = sprintf(__(
+                                    /* Phrase will read:
+                                       <a> of <b> <selected objects> made PRIVATE */
+                                    '%1$d of %2$d %3$s made PRIVATE'), $num, $count,
+                                    _N('selected department', 'selected departments', $count));
                         } else {
-                            $errors['err'] = 'Unable to make selected department(s) private. Possibly already private!';
+                            $errors['err'] = sprintf(__('Unable to make %s private. Possibly already private!'),
+                                _N('selected department', 'selected departments', $count));
                         }
                         break;
                     case 'delete':
@@ -76,7 +93,7 @@ if($_POST){
                             .' WHERE dept_id IN ('.implode(',', db_input($_POST['ids'])).')';
                         list($members)=db_fetch_row(db_query($sql));
                         if($members)
-                            $errors['err']='Departments with staff can not be deleted. Move staff first.';
+                            $errors['err']=__('Departments with agents can not be deleted. Move the agents first.');
                         else {
                             $i=0;
                             foreach($_POST['ids'] as $k=>$v) {
@@ -84,20 +101,26 @@ if($_POST){
                                     $i++;
                             }
                             if($i && $i==$count)
-                                $msg = 'Selected departments deleted successfully';
+                                $msg = sprintf(__('Successfully deleted %s'),
+                                    _N('selected department', 'selected departments', $count));
                             elseif($i>0)
-                                $warn = "$i of $count selected departments deleted";
+                                $warn = sprintf(__(
+                                    /* Phrase will read:
+                                       <a> of <b> <selected objects> deleted */
+                                    '%1$d of %2$d %3$s deleted'), $i, $count,
+                                    _N('selected department', 'selected departments', $count));
                             elseif(!$errors['err'])
-                                $errors['err'] = 'Unable to delete selected departments.';
+                                $errors['err'] = sprintf(__('Unable to delete %s.'),
+                                    _N('selected department', 'selected departments', $count));
                         }
                         break;
                     default:
-                        $errors['err']='Unknown action - get technical help';
+                        $errors['err']=__('Unknown action - get technical help.');
                 }
             }
             break;
         default:
-            $errors['err']='Unknown action/command';
+            $errors['err']=__('Unknown action');
             break;
     }
 }
diff --git a/scp/emails.php b/scp/emails.php
index 8c688fcb99a50f2640ee0e20a60d149f69274f31..4e8bf4befa5db5e0b136000acb2d4d170dabf572 100644
--- a/scp/emails.php
+++ b/scp/emails.php
@@ -18,30 +18,33 @@ include_once(INCLUDE_DIR.'class.email.php');
 
 $email=null;
 if($_REQUEST['id'] && !($email=Email::lookup($_REQUEST['id'])))
-    $errors['err']='Unknown or invalid email ID.';
+    $errors['err']=sprintf(__('%s: Unknown or invalid ID.'), __('email'));
 
 if($_POST){
     switch(strtolower($_POST['do'])){
         case 'update':
             if(!$email){
-                $errors['err']='Unknown or invalid email.';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('email'));
             }elseif($email->update($_POST,$errors)){
-                $msg='Email updated successfully';
+                $msg=sprintf(__('Successfully updated %s'),
+                    __('this email'));
             }elseif(!$errors['err']){
-                $errors['err']='Error updating email. Try again!';
+                $errors['err']=sprintf(__('Error updating %s. Try again!'), __('this email'));
             }
             break;
         case 'create':
             if(($id=Email::create($_POST,$errors))){
-                $msg='Email address added successfully';
+                $msg=sprintf(__('Successfully added %s'), Format::htmlchars($_POST['name']));
                 $_REQUEST['a']=null;
             }elseif(!$errors['err']){
-                $errors['err']='Unable to add email. Correct error(s) below and try again.';
+                $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                    __('this email'));
             }
             break;
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err'] = 'You must select at least one email address';
+                $errors['err'] = sprintf(__('You must select at least %s'),
+                    __('one email'));
             } else {
                 $count=count($_POST['ids']);
 
@@ -51,7 +54,7 @@ if($_POST){
 
                 list($depts)=db_fetch_row(db_query($sql));
                 if($depts>0) {
-                    $errors['err'] = 'One or more of the selected emails is being used by a department. Remove association first!';
+                    $errors['err'] = __('One or more of the selected emails is being used by a department. Remove association first!');
                 } elseif(!strcasecmp($_POST['a'], 'delete')) {
                     $i=0;
                     foreach($_POST['ids'] as $k=>$v) {
@@ -60,19 +63,22 @@ if($_POST){
                     }
 
                     if($i && $i==$count)
-                        $msg = 'Selected emails deleted successfully';
+                        $msg = sprintf(__('Successfully deleted %s'),
+                            _N('selected email', 'selected emails', $count));
                     elseif($i>0)
-                        $warn = "$i of $count selected emails deleted";
+                        $warn = sprintf(__('%1$d of %2$d %3$s deleted'), $i, $count,
+                            _N('selected email', 'selected emails', $count));
                     elseif(!$errors['err'])
-                        $errors['err'] = 'Unable to delete selected emails';
+                        $errors['err'] = sprintf(__('Unable to delete %s'),
+                            _N('selected email', 'selected emails', $count));
 
                 } else {
-                    $errors['err'] = 'Unknown action - get technical help';
+                    $errors['err'] = __('Unknown action - get technical help.');
                 }
             }
             break;
         default:
-            $errors['err'] = 'Unknown action/command';
+            $errors['err'] = __('Unknown action');
             break;
     }
 }
diff --git a/scp/emailtest.php b/scp/emailtest.php
index 6941944246f31cd824dc96dd8f9c3308cec26dc0..c714ef1d0643d6c480aac316b511c851244754ef 100644
--- a/scp/emailtest.php
+++ b/scp/emailtest.php
@@ -23,28 +23,29 @@ if($_POST){
     $errors=array();
     $email=null;
     if(!$_POST['email_id'] || !($email=Email::lookup($_POST['email_id'])))
-        $errors['email_id']='Select from email address';
+        $errors['email_id']=__('Select from email address');
 
     if(!$_POST['email'] || !Validator::is_email($_POST['email']))
-        $errors['email']='To email address required';
+        $errors['email']=__('To email address required');
 
     if(!$_POST['subj'])
-        $errors['subj']='Subject required';
+        $errors['subj']=__('Subject required');
 
     if(!$_POST['message'])
-        $errors['message']='Message required';
+        $errors['message']=__('Message required');
 
     if(!$errors && $email){
         if($email->send($_POST['email'],$_POST['subj'],
                 Format::sanitize($_POST['message']),
                 null, array('reply-tag'=>false))) {
-            $msg='Test email sent successfully to '.Format::htmlchars($_POST['email']);
+            $msg=Format::htmlchars(sprintf(__('Test email sent successfully to <%s>'),
+                $_POST['email']));
             Draft::deleteForNamespace('email.diag');
         }
         else
-            $errors['err']='Error sending email - try again.';
+            $errors['err']=__('Error sending email - try again.');
     }elseif($errors['err']){
-        $errors['err']='Error sending email - try again.';
+        $errors['err']=__('Error sending email - try again.');
     }
 }
 $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
@@ -56,23 +57,24 @@ require(STAFFINC_DIR.'header.inc.php');
 <form action="emailtest.php" method="post" id="save">
  <?php csrf_token(); ?>
  <input type="hidden" name="do" value="<?php echo $action; ?>">
- <h2>Test Outgoing Email</h2>
+ <h2><?php echo __('Test Outgoing Email');?></h2>
  <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2">
     <thead>
         <tr>
             <th colspan="2">
-                <em>Use the following form to test whether your <strong>Outgoing Email</strong> settings are properly established.&nbsp;<i class="help-tip icon-question-sign" href="#test_outgoing_email"></i></em>
+                <em><?php echo __('Use the following form to test whether your <strong>Outgoing Email</strong> settings are properly established.');
+                    ?>&nbsp;<i class="help-tip icon-question-sign" href="#test_outgoing_email"></i></em>
             </th>
         </tr>
     </thead>
     <tbody>
         <tr>
             <td width="120" class="required">
-                From:
+                <?php echo __('From');?>:
             </td>
             <td>
                 <select name="email_id">
-                    <option value="0">&mdash; Select FROM Email &mdash;</option>
+                    <option value="0">&mdash; <?php echo __('Select FROM Email');?> &mdash;</option>
                     <?php
                     $sql='SELECT email_id,email,name,smtp_active FROM '.EMAIL_TABLE.' email ORDER by name';
                     if(($res=db_query($sql)) && db_num_rows($res)){
@@ -81,7 +83,7 @@ require(STAFFINC_DIR.'header.inc.php');
                             if($name)
                                 $email=Format::htmlchars("$name <$email>");
                             if($smtp)
-                                $email.=' (SMTP)';
+                                $email.=' ('.__('SMTP').')';
 
                             echo sprintf('<option value="%d" %s>%s</option>',$id,$selected,$email);
                         }
@@ -93,7 +95,7 @@ require(STAFFINC_DIR.'header.inc.php');
         </tr>
         <tr>
             <td width="120" class="required">
-                To:
+                <?php echo __('To');?>:
             </td>
             <td>
                 <input type="text" size="60" name="email" value="<?php echo $info['email']; ?>">
@@ -102,7 +104,7 @@ require(STAFFINC_DIR.'header.inc.php');
         </tr>
         <tr>
             <td width="120" class="required">
-                Subject:
+                <?php echo __('Subject');?>:
             </td>
             <td>
                 <input type="text" size="60" name="subj" value="<?php echo $info['subj']; ?>">
@@ -112,7 +114,7 @@ require(STAFFINC_DIR.'header.inc.php');
         <tr>
             <td colspan=2>
                 <div style="padding-top:0.5em;padding-bottom:0.5em">
-                <em><strong>Message</strong>: email message to send.</em>&nbsp;<span class="error">*&nbsp;<?php echo $errors['message']; ?></span></div>
+                <em><strong><?php echo __('Message');?></strong>: <?php echo __('email message to send.');?></em>&nbsp;<span class="error">*&nbsp;<?php echo $errors['message']; ?></span></div>
                 <textarea class="richtext draft draft-delete" name="message" cols="21"
                     data-draft-namespace="email.diag"
                     rows="10" style="width: 90%;"><?php echo $info['message']; ?></textarea>
@@ -121,9 +123,9 @@ require(STAFFINC_DIR.'header.inc.php');
     </tbody>
 </table>
 <p style="padding-left:225px;">
-    <input type="submit" name="submit" value="Send Message">
-    <input type="reset"  name="reset"  value="Reset">
-    <input type="button" name="cancel" value="Cancel" onclick='window.location.href="emails.php"'>
+    <input type="submit" name="submit" value="<?php echo __('Send Message');?>">
+    <input type="reset"  name="reset"  value="<?php echo __('Reset');?>">
+    <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="emails.php"'>
 </p>
 </form>
 <?php
diff --git a/scp/faq.php b/scp/faq.php
index 602b4a33e2eca89b7529715ddd2a58b1001c1569..2a3c2e95116e2b6da074df0e0655366d581ddec3 100644
--- a/scp/faq.php
+++ b/scp/faq.php
@@ -18,39 +18,49 @@ require_once(INCLUDE_DIR.'class.faq.php');
 
 $faq=$category=null;
 if($_REQUEST['id'] && !($faq=FAQ::lookup($_REQUEST['id'])))
-   $errors['err']='Unknown or invalid FAQ';
+    $errors['err']=sprintf(__('%s: Unknown or invalid'), __('FAQ article'));
 
 if($_REQUEST['cid'] && !$faq && !($category=Category::lookup($_REQUEST['cid'])))
-    $errors['err']='Unknown or invalid FAQ category';
+    $errors['err']=sprintf(__('%s: Unknown or invalid'), __('FAQ category'));
+
+$faq_form = new Form(array(
+    'attachments' => new FileUploadField(array('id'=>'attach',
+        'configuration'=>array('extensions'=>false,
+            'size'=>$cfg->getMaxFileSize())
+   )),
+));
 
 if($_POST):
     $errors=array();
+    $_POST['files'] = $faq_form->getField('attachments')->getClean();
     switch(strtolower($_POST['do'])) {
         case 'create':
         case 'add':
             if(($faq=FAQ::add($_POST,$errors))) {
-                $msg='FAQ added successfully';
+                $msg=sprintf(__('Successfully added %s'), Format::htmlchars($faq->getQuestion()));
                 // Delete draft for this new faq
                 Draft::deleteForNamespace('faq', $thisstaff->getId());
             } elseif(!$errors['err'])
-                $errors['err'] = 'Unable to add FAQ. Try again!';
+                $errors['err'] = sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                     __('this FAQ article'));
         break;
         case 'update':
         case 'edit';
             if(!$faq)
-                $errors['err'] = 'Invalid or unknown FAQ';
+                $errors['err'] = sprintf(__('%s: Invalid or unknown'), __('FAQ article'));
             elseif($faq->update($_POST,$errors)) {
-                $msg='FAQ updated successfully';
+                $msg=sprintf(__('Successfully updated %s'), __('this FAQ article'));
                 $_REQUEST['a']=null; //Go back to view
                 $faq->reload();
                 // Delete pending draft updates for this faq (for ALL users)
                 Draft::deleteForNamespace('faq.'.$faq->getId());
             } elseif(!$errors['err'])
-                $errors['err'] = 'Unable to update FAQ. Try again!';
+                $errors['err'] = sprintf(__('Unable to update %s. Correct error(s) below and try again.'),
+                    __('this FAQ article'));
             break;
         case 'manage-faq':
             if(!$faq) {
-                $errors['err']='Unknown or invalid FAQ';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('FAQ article'));
             } else {
                 switch(strtolower($_POST['a'])) {
                     case 'edit':
@@ -58,32 +68,33 @@ if($_POST):
                         break;
                     case 'publish';
                         if($faq->publish())
-                            $msg='FAQ published successfully';
+                            $msg=sprintf(__('Successfully published %s'), __('this FAQ article'));
                         else
-                            $errors['err']='Unable to publish the FAQ. Try editing it.';
+                            $errors['err']=sprintf(__('Unable to publish %s. Try editing it.'),
+                                __('this FAQ article'));
                         break;
                     case 'unpublish';
                         if($faq->unpublish())
-                            $msg='FAQ unpublished successfully';
+                            $msg=sprintf(__('Successfully unpublished %s'), __('this FAQ article'));
                         else
-                            $errors['err']='Unable to unpublish the FAQ. Try editing it.';
+                            $errors['err']=sprintf(__('Unable to unpublish %s. Try editing it.'), __('this FAQ article'));
                         break;
                     case 'delete':
                         $category = $faq->getCategory();
                         if($faq->delete()) {
-                            $msg='FAQ deleted successfully';
+                            $msg=sprintf(__('Successfully deleted %s'), Format::htmlchars($faq->getQuestion()));
                             $faq=null;
                         } else {
-                            $errors['err']='Unable to delete FAQ. Try again';
+                            $errors['err']=sprintf(__('Unable to delete %s.'), __('this FAQ article'));
                         }
                         break;
                     default:
-                        $errors['err']='Invalid action';
+                        $errors['err']=__('Invalid action');
                 }
             }
             break;
         default:
-            $errors['err']='Unknown action';
+            $errors['err']=__('Unknown action');
 
     }
 endif;
@@ -105,5 +116,6 @@ $ost->addExtraHeader('<meta name="tip-namespace" content="' . $tip_namespace . '
     "$('#content').data('tipNamespace', '".$tip_namespace."');");
 require_once(STAFFINC_DIR.'header.inc.php');
 require_once(STAFFINC_DIR.$inc);
+print $faq_form->getMedia();
 require_once(STAFFINC_DIR.'footer.inc.php');
 ?>
diff --git a/scp/file.php b/scp/file.php
index 68197cc566cf05f707d7d0d458b7097422b7b8ae..97687de0f0bb0a341ce895298f587891a9c179c0 100644
--- a/scp/file.php
+++ b/scp/file.php
@@ -1,9 +1,9 @@
 <?php
 /*********************************************************************
     file.php
-    
+
     Simply downloads the file...on hash validation as follows;
-    
+
     * Hash must be 64 chars long.
     * First 32 chars is the perm. file hash
     * Next 32 chars  is md5(file_id.session_id().file_hash)
@@ -24,7 +24,7 @@ $h=trim($_GET['h']);
 if(!$h  || strlen($h)!=64  //32*2
         || !($file=AttachmentFile::lookup(substr($h,0,32))) //first 32 is the file hash.
         || strcasecmp($file->getDownloadHash(), $h)) //next 32 is file id + session hash.
-    die('Unknown or invalid file. #'.Format::htmlchars($_GET['h']));
+    Http::response(404, __('Unknown or invalid file'));
 
 $file->download();
 ?>
diff --git a/scp/filters.php b/scp/filters.php
index 99d1f13e71d68d358890f52af3b6081fd284d7cb..57b05e526135a9a1d3e34bd56fad2b547bc31008 100644
--- a/scp/filters.php
+++ b/scp/filters.php
@@ -16,36 +16,40 @@
 require('admin.inc.php');
 include_once(INCLUDE_DIR.'class.filter.php');
 require_once(INCLUDE_DIR.'class.canned.php');
+
 $filter=null;
 if($_REQUEST['id'] && !($filter=Filter::lookup($_REQUEST['id'])))
-    $errors['err']='Unknown or invalid filter.';
+    $errors['err']=sprintf(__('%s: Unknown or invalid'), __('ticket filter'));
 
 /* NOTE: Banlist has its own interface*/
 if($filter && $filter->isSystemBanlist())
-    header('Location: banlist.php');
+    Http::redirect('banlist.php');
 
 if($_POST){
     switch(strtolower($_POST['do'])){
         case 'update':
             if(!$filter){
-                $errors['err']='Unknown or invalid filter.';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('ticket filter'));
             }elseif($filter->update($_POST,$errors)){
-                $msg='Filter updated successfully';
+                $msg=sprintf(__('Successfully updated %s'), __('this ticket filter'));
             }elseif(!$errors['err']){
-                $errors['err']='Error updating filter. Try again!';
+                $errors['err']=sprintf(__('Error updating %s. Correct error(s) below and try again.'),
+                    __('this ticket filter'));
             }
             break;
         case 'add':
             if((Filter::create($_POST,$errors))){
-                $msg='Filter added successfully';
+                $msg=sprintf(__('Successfully updated %s'), __('this ticket filter'));
                 $_REQUEST['a']=null;
             }elseif(!$errors['err']){
-                $errors['err']='Unable to add filter. Correct error(s) below and try again.';
+                $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                    __('this ticket filter'));
             }
             break;
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err'] = 'You must select at least one filter to process.';
+                $errors['err'] = sprintf(__('You must select at least %s to process.'),
+                    __('one ticket filter'));
             } else {
                 $count=count($_POST['ids']);
                 switch(strtolower($_POST['a'])) {
@@ -54,11 +58,14 @@ if($_POST){
                             .' WHERE id IN ('.implode(',', db_input($_POST['ids'])).')';
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected filters enabled';
+                                $msg = sprintf(__('Successfully enabled %s'),
+                                    _N('selected ticket filter', 'selected ticket filters', $count));
                             else
-                                $warn = "$num of $count selected filters enabled";
+                                $warn = sprintf(__('%1$d of %2$d %3$s enabled'), $num, $count,
+                                    _N('selected ticket filter', 'selected ticket filters', $count));
                         } else {
-                            $errors['err'] = 'Unable to enable selected filters';
+                            $errors['err'] = sprintf(__('Unable to enable %s'),
+                                _N('selected ticket filter', 'selected ticket filters', $count));
                         }
                         break;
                     case 'disable':
@@ -66,11 +73,14 @@ if($_POST){
                             .' WHERE id IN ('.implode(',', db_input($_POST['ids'])).')';
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected filters disabled';
+                                $msg = sprintf(__('Successfully disabled %s'),
+                                    _N('selected ticket filter', 'selected ticket filters', $count));
                             else
-                                $warn = "$num of $count selected filters disabled";
+                                $warn = sprintf(__('%1$d of %2$d %3$s disabled'), $num, $count,
+                                    _N('selected ticket filter', 'selected ticket filters', $count));
                         } else {
-                            $errors['err'] = 'Unable to disable selected filters';
+                            $errors['err'] = sprintf(__('Unable to disable %s'),
+                                _N('selected ticket filter', 'selected ticket filters', $count));
                         }
                         break;
                     case 'delete':
@@ -81,19 +91,22 @@ if($_POST){
                         }
 
                         if($i && $i==$count)
-                            $msg = 'Selected filters deleted successfully';
+                            $msg = sprintf(__('Successfully deleted %s'),
+                                _N('selected ticket filter', 'selected ticket filters', $count));
                         elseif($i>0)
-                            $warn = "$i of $count selected filters deleted";
+                            $warn = sprintf(__('%1$d of %2$d %s deleted'), $i, $count,
+                                _N('selected ticket filter', 'selected ticket filters', $count));
                         elseif(!$errors['err'])
-                            $errors['err'] = 'Unable to delete selected filters';
+                            $errors['err'] = sprintf(__('Unable to delete %s'),
+                                 _N('selected ticket filter', 'selected ticket filters', $count));
                         break;
                     default:
-                        $errors['err']='Unknown action - get technical help';
+                        $errors['err']=__('Unknown action - get technical help.');
                 }
             }
             break;
         default:
-            $errors['err']='Unknown commande/action';
+            $errors['err']=__('Unknown command/action');
             break;
     }
 }
diff --git a/scp/forms.php b/scp/forms.php
index 245ed33712b80c508ad47c68ee0d4b225265c820..3b7e6af04e7f137af5732a6d7866fb169fd2a495 100644
--- a/scp/forms.php
+++ b/scp/forms.php
@@ -4,23 +4,23 @@ require_once(INCLUDE_DIR."/class.dynamic_forms.php");
 
 $form=null;
 if($_REQUEST['id'] && !($form=DynamicForm::lookup($_REQUEST['id'])))
-    $errors['err']='Unknown or invalid dynamic form ID.';
+    $errors['err']=sprintf(__('%s: Unknown or invalid ID.'), __('custom form'));
 
 if($_POST) {
     $fields = array('title', 'notes', 'instructions');
     $required = array('title');
     $max_sort = 0;
     $form_fields = array();
+    $names = array();
     switch(strtolower($_POST['do'])) {
         case 'update':
             foreach ($fields as $f)
                 if (in_array($f, $required) && !$_POST[$f])
-                    $errors[$f] = sprintf('%s is required',
+                    $errors[$f] = sprintf(__('%s is required'),
                         mb_convert_case($f, MB_CASE_TITLE));
                 elseif (isset($_POST[$f]))
                     $form->set($f, $_POST[$f]);
             $form->save(true);
-            $names = array();
             foreach ($form->getDynamicFields() as $field) {
                 $id = $field->get('id');
                 if ($_POST["delete-$id"] == 'on' && $field->isDeletable()) {
@@ -36,26 +36,21 @@ if($_POST) {
                 if (isset($_POST["type-$id"]) && $field->isChangeable())
                     $field->set('type', $_POST["type-$id"]);
                 if (isset($_POST["name-$id"]) && !$field->isNameForced())
-                    $field->set('name', $_POST["name-$id"]);
+                    $field->set('name', trim($_POST["name-$id"]));
                 # TODO: make sure all help topics still have all required fields
-                if (!$field->isRequirementForced())
-                    $field->set('required', $_POST["required-$id"] == 'on' ?  1 : 0);
-                if (!$field->isPrivacyForced())
-                    $field->set('private', $_POST["private-$id"] == 'on' ?  1 : 0);
+                $field->setRequirementMode($_POST["visibility-$id"]);
+
                 foreach (array('sort','label') as $f) {
                     if (isset($_POST["$f-$id"])) {
                         $field->set($f, $_POST["$f-$id"]);
                     }
                 }
                 if (in_array($field->get('name'), $names))
-                    $field->addError('Field variable name is not unique', 'name');
-                if (preg_match('/[.{}\'"`; ]/u', $field->get('name')))
-                    $field->addError('Invalid character in variable name. Please use letters and numbers only.', 'name');
+                    $field->addError(__('Field variable name is not unique'), 'name');
                 // Subject (Issue Summary) must always have data
                 if ($form->get('type') == 'T' && $field->get('name') == 'subject') {
                     if (($f = $field->getField(false)->getImpl()) && !$f->hasData())
-                        $field->addError('The issue summary must be a field '
-                            .'that supports user input, such as short answer',
+                        $field->addError(__('The issue summary must be a field that supports user input, such as short answer'),
                             'type');
                 }
                 if ($field->get('name'))
@@ -64,7 +59,7 @@ if($_POST) {
                     $form_fields[] = $field;
                 else
                     # notrans (not shown)
-                    $errors["field-$id"] = 'Field has validation errors';
+                    $errors["field-$id"] = __('Field has validation errors');
                 // Keep track of the last sort number
                 $max_sort = max($max_sort, $field->get('sort'));
             }
@@ -82,7 +77,7 @@ if($_POST) {
 
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err'] = 'You must select at least one API key';
+                $errors['err'] = sprintf(__('You must select at least %s'), __('one custom form'));
             } else {
                 $count = count($_POST['ids']);
                 switch(strtolower($_POST['a'])) {
@@ -93,11 +88,14 @@ if($_POST) {
                                 $i++;
                         }
                         if ($i && $i==$count)
-                            $msg = 'Selected custom forms deleted successfully';
+                            $msg = sprintf(__('Successfully deleted %s'),
+                                _N('selected custom form', 'selected custom forms', $count));
                         elseif ($i > 0)
-                            $warn = "$i of $count selected forms deleted";
+                            $warn = sprintf(__('%1$d of %1$d %3$s deleted'), $i, $count,
+                                _N('selected custom form', 'selected custom forms', $count));
                         elseif (!$errors['err'])
-                            $errors['err'] = 'Unable to delete selected custom forms';
+                            $errors['err'] = sprintf(__('Unable to delete %s'),
+                                _N('selected custom form', 'selected custom forms', $count));
                         break;
                 }
             }
@@ -112,13 +110,17 @@ if($_POST) {
                 'sort'=>$_POST["sort-new-$i"] ? $_POST["sort-new-$i"] : ++$max_sort,
                 'label'=>$_POST["label-new-$i"],
                 'type'=>$_POST["type-new-$i"],
-                'name'=>$_POST["name-new-$i"],
-                'private'=>$_POST["private-new-$i"] == 'on' ? 1 : 0,
-                'required'=>$_POST["required-new-$i"] == 'on' ? 1 : 0
+                'name'=>trim($_POST["name-new-$i"]),
             ));
+            $field->setRequirementMode($_POST["visibility-new-$i"]);
             $field->setForm($form);
-            if ($field->isValid())
+            if (in_array($field->get('name'), $names))
+                $field->addError(__('Field variable name is not unique'), 'name');
+            if ($field->isValid()) {
                 $form_fields[] = $field;
+                if ($N = $field->get('name'))
+                    $names[] = $N;
+            }
             else
                 $errors["new-$i"] = $field->errors();
         }
@@ -135,9 +137,10 @@ if($_POST) {
         }
     }
     if ($errors)
-        $errors['err'] = 'Unable to commit form. Check validation errors';
+        $errors['err'] = sprintf(__('Unable to commit %s. Check validation errors'), __('this custom form'));
     else
-        $msg = 'Custom form successfully updated';
+        $msg = sprintf(__('Successfully updated %s'),
+            __('this custom form'));
 }
 
 $page='dynamic-forms.inc.php';
diff --git a/scp/groups.php b/scp/groups.php
index b2cbd0466dac6fcf166d049367974f1428a713a6..c3f17f9c22e7f66b3e239bdf5943e623ccafb5e5 100644
--- a/scp/groups.php
+++ b/scp/groups.php
@@ -14,34 +14,38 @@
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
 require('admin.inc.php');
+
 $group=null;
 if($_REQUEST['id'] && !($group=Group::lookup($_REQUEST['id'])))
-    $errors['err']='Unknown or invalid group ID.';
+    $errors['err']=sprintf(__('%s: Unknown or invalid ID.'), __('group'));
 
 if($_POST){
     switch(strtolower($_POST['do'])){
         case 'update':
             if(!$group){
-                $errors['err']='Unknown or invalid group.';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('group'));
             }elseif($group->update($_POST,$errors)){
-                $msg='Group updated successfully';
+                $msg=sprintf(__('Successfully updated %s'),
+                    __('this group'));
             }elseif(!$errors['err']){
-                $errors['err']='Unable to update group. Correct any error(s) below and try again!';
+                $errors['err']=sprintf(__('Unable to update %s. Correct error(s) below and try again!'),
+                    __('this group'));
             }
             break;
         case 'create':
             if(($id=Group::create($_POST,$errors))){
-                $msg=Format::htmlchars($_POST['name']).' added successfully';
+                $msg=sprintf(__('Successfully added %s'),Format::htmlchars($_POST['name']));
                 $_REQUEST['a']=null;
             }elseif(!$errors['err']){
-                $errors['err']='Unable to add group. Correct error(s) below and try again.';
+                $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                    __('this group'));
             }
             break;
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err'] = 'You must select at least one group.';
+                $errors['err'] = sprintf(__('You must select at least %s.'), __('one group'));
             } elseif(in_array($thisstaff->getGroupId(), $_POST['ids'])) {
-                $errors['err'] = "As an admin, you can't disable/delete a group you belong to - you might lockout all admins!";
+                $errors['err'] = __("As an admin, you cannot disable/delete a group you belong to - you might lockout all admins!");
             } else {
                 $count=count($_POST['ids']);
                 switch(strtolower($_POST['a'])) {
@@ -51,11 +55,14 @@ if($_POST){
 
                         if(db_query($sql) && ($num=db_affected_rows())){
                             if($num==$count)
-                                $msg = 'Selected groups activated';
+                                $msg = sprintf(__('Successfully activated %s'),
+                                    _N('selected group', 'selected groups', $count));
                             else
-                                $warn = "$num of $count selected groups activated";
+                                $warn = sprintf(__('%1$d of %2$d %3$s activated'), $num, $count,
+                                    _N('selected group', 'selected groups', $count));
                         } else {
-                            $errors['err'] = 'Unable to activate selected groups';
+                            $errors['err'] = sprintf(__('Unable to activate %s'),
+                                _N('selected group', 'selected groups', $count));
                         }
                         break;
                     case 'disable':
@@ -63,11 +70,14 @@ if($_POST){
                             .' WHERE group_id IN ('.implode(',', db_input($_POST['ids'])).')';
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected groups disabled';
+                                $msg = sprintf(__('Successfully disabled %s'),
+                                    _N('selected group', 'selected groups', $count));
                             else
-                                $warn = "$num of $count selected groups disabled";
+                                $warn = sprintf(__('%1$d of %2$d %3$s disabled'), $num, $count,
+                                    _N('selected group', 'selected groups', $count));
                         } else {
-                            $errors['err'] = 'Unable to disable selected groups';
+                            $errors['err'] = sprintf(__('Unable to disable %s'),
+                                _N('selected group', 'selected groups', $count));
                         }
                         break;
                     case 'delete':
@@ -77,19 +87,22 @@ if($_POST){
                         }
 
                         if($i && $i==$count)
-                            $msg = 'Selected groups deleted successfully';
+                            $msg = sprintf(__('Successfully deleted %s'),
+                                _N('selected group', 'selected groups', $count));
                         elseif($i>0)
-                            $warn = "$i of $count selected groups deleted";
+                            $warn = sprintf(__('%1$d of %2$d %3$s deleted'), $i, $count,
+                                _N('selected group', 'selected groups', $count));
                         elseif(!$errors['err'])
-                            $errors['err'] = 'Unable to delete selected groups';
+                            $errors['err'] = sprintf(__('Unable to delete %s'),
+                                _N('selected group', 'selected groups', $count));
                         break;
                     default:
-                        $errors['err']  = 'Unknown action. Get technical help!';
+                        $errors['err']  = __('Unknown action - get technical help.');
                 }
             }
             break;
         default:
-            $errors['err']='Unknown action';
+            $errors['err']=__('Unknown action');
             break;
     }
 }
diff --git a/scp/helptopics.php b/scp/helptopics.php
index b33cad9a5e79b77fae4adce850a6af88c36dc943..44af9f9c9bb9fa233e6259837b845d8166ad814d 100644
--- a/scp/helptopics.php
+++ b/scp/helptopics.php
@@ -19,25 +19,28 @@ require_once(INCLUDE_DIR.'class.dynamic_forms.php');
 
 $topic=null;
 if($_REQUEST['id'] && !($topic=Topic::lookup($_REQUEST['id'])))
-    $errors['err']='Unknown or invalid help topic ID.';
+    $errors['err']=sprintf(__('%s: Unknown or invalid ID.'), __('help topic'));
 
 if($_POST){
     switch(strtolower($_POST['do'])){
         case 'update':
             if(!$topic){
-                $errors['err']='Unknown or invalid help topic.';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('help topic'));
             }elseif($topic->update($_POST,$errors)){
-                $msg='Help topic updated successfully';
+                $msg=sprintf(__('Successfully updated %s'),
+                    __('this help topic'));
             }elseif(!$errors['err']){
-                $errors['err']='Error updating help topic. Try again!';
+                $errors['err']=sprintf(__('Error updating %s. Try again!'),
+                    __('this help topic'));
             }
             break;
         case 'create':
             if(($id=Topic::create($_POST,$errors))){
-                $msg='Help topic added successfully';
+                $msg=sprintf(__('Successfully added %s'), Format::htmlchars($_POST['topic']));
                 $_REQUEST['a']=null;
             }elseif(!$errors['err']){
-                $errors['err']='Unable to add help topic. Correct error(s) below and try again.';
+                $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                    __('this help topic'));
             }
             break;
         case 'mass_process':
@@ -47,7 +50,8 @@ if($_POST){
                 break;
             default:
                 if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids']))
-                    $errors['err'] = 'You must select at least one help topic';
+                    $errors['err'] = sprintf(__('You must select at least %s'),
+                        __('one help topic'));
             }
             if (!$errors) {
                 $count=count($_POST['ids']);
@@ -59,11 +63,14 @@ if($_POST){
 
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected help topics enabled';
+                                $msg = sprintf(__('Successfully enabled %s'),
+                                    _N('selected help topic', 'selected help topics', $count));
                             else
-                                $warn = "$num of $count selected help topics enabled";
+                                $warn = sprintf(__('%1$d of %2$d %3$s enabled'), $num, $count,
+                                    _N('selected help topic', 'selected help topics', $count));
                         } else {
-                            $errors['err'] = 'Unable to enable selected help topics.';
+                            $errors['err'] = sprintf(__('Unable to enable %s.'),
+                                _N('selected help topic', 'selected help topics', $count));
                         }
                         break;
                     case 'disable':
@@ -72,11 +79,14 @@ if($_POST){
                             .' AND topic_id <> '.db_input($cfg->getDefaultTopicId());
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected help topics disabled';
+                                $msg = sprintf(__('Successfully diabled %s'),
+                                    _N('selected help topic', 'selected help topics', $count));
                             else
-                                $warn = "$num of $count selected help topics disabled";
+                                $warn = sprintf(__('%1$d of %2$d %3$s disabled'), $num, $count,
+                                    _N('selected help topic', 'selected help topics', $count));
                         } else {
-                            $errors['err'] ='Unable to disable selected help topic(s)';
+                            $errors['err'] = sprintf(__('Unable to disable %s'),
+                                _N('selected help topic', 'selected help topics', $count));
                         }
                         break;
                     case 'delete':
@@ -87,11 +97,14 @@ if($_POST){
                         }
 
                         if($i && $i==$count)
-                            $msg = 'Selected help topics deleted successfully';
+                            $msg = sprintf(__('Successfully deleted %s'),
+                                _N('selected help topic', 'selected elp topics', $count));
                         elseif($i>0)
-                            $warn = "$i of $count selected help topics deleted";
+                            $warn = sprintf(__('%1$d of %2$d %3$s deleted'), $i, $count,
+                                _N('selected help topic', 'selected help topics', $count));
                         elseif(!$errors['err'])
-                            $errors['err']  = 'Unable to delete selected help topics';
+                            $errors['err']  = sprintf(__('Unable to delete %s'),
+                                _N('selected help topic', 'selected help topics', $count));
 
                         break;
                     case 'sort':
@@ -105,19 +118,19 @@ if($_POST){
                                         $t->setSortOrder($v);
                                 }
                             }
-                            $msg = 'Successfully set sorting configuration';
+                            $msg = __('Successfully set sorting configuration');
                         }
                         catch (Exception $ex) {
-                            $errors['err'] = 'Unable to set sorting mode';
+                            $errors['err'] = __('Unable to set sorting mode');
                         }
                         break;
                     default:
-                        $errors['err']='Unknown action - get technical help.';
+                        $errors['err']=__('Unknown action - get technical help.');
                 }
             }
             break;
         default:
-            $errors['err']='Unknown command/action';
+            $errors['err']=__('Unknown action');
             break;
     }
     if ($id or $topic) {
diff --git a/scp/image.php b/scp/image.php
index f10fcd6e9039817022330ee87aa00009127888a2..4c4ddbfe6da36e21588c0359b96924c365fba64b 100644
--- a/scp/image.php
+++ b/scp/image.php
@@ -25,7 +25,7 @@ $h=trim($_GET['h']);
 if(!$h  || strlen($h)!=64  //32*2
         || !($file=AttachmentFile::lookup(substr($h,0,32))) //first 32 is the file hash.
         || strcasecmp($h, $file->getDownloadHash())) //next 32 is file id + session hash.
-    Http::response(404, 'Unknown or invalid file');
+    Http::response(404, __('Unknown or invalid file'));
 
 if ($_GET['s'] && is_numeric($_GET['s']))
     $file->display($_GET['s']);
diff --git a/scp/images/tab-bg.png b/scp/images/tab-bg.png
deleted file mode 100644
index 16262493293054bc124fd54021655f4a20c63afc..0000000000000000000000000000000000000000
Binary files a/scp/images/tab-bg.png and /dev/null differ
diff --git a/scp/js/jquery.dropdown.js b/scp/js/jquery.dropdown.js
index c0604e75d2686c457c5ccb56c4c7be9122dbe970..b885042086efee07eeb228c60abefd86ec278c0b 100644
--- a/scp/js/jquery.dropdown.js
+++ b/scp/js/jquery.dropdown.js
@@ -36,7 +36,8 @@ if(jQuery) (function($) {
 
 		var trigger = $(this),
 			dropdown = $( $(this).attr('data-dropdown') ),
-			isOpen = trigger.hasClass('dropdown-open');
+			isOpen = trigger.hasClass('dropdown-open'),
+            rtl = $('html').hasClass('rtl');
 
 		event.preventDefault();
 		event.stopPropagation();
@@ -45,6 +46,9 @@ if(jQuery) (function($) {
 
 		if( isOpen || trigger.hasClass('dropdown-disabled') ) return;
 
+        if (rtl && dropdown.hasClass('anchor-right'))
+            dropdown.removeClass('anchor-right');
+
 		dropdown.css({
 				left: dropdown.hasClass('anchor-right') ?
 				trigger.offset().left - (dropdown.outerWidth() - trigger.outerWidth() - 4) : trigger.offset().left,
diff --git a/scp/js/nicEdit.js b/scp/js/nicEdit.js
deleted file mode 100644
index 37b66e2a3dc7c7ebcf449e34e2e29dda43c7607c..0000000000000000000000000000000000000000
--- a/scp/js/nicEdit.js
+++ /dev/null
@@ -1,112 +0,0 @@
-/* NicEdit - Micro Inline WYSIWYG
- * Copyright 2007-2008 Brian Kirchoff
- *
- * NicEdit is distributed under the terms of the MIT license
- * For more information visit http://nicedit.com/
- * Do not remove this copyright message
- */
-var bkExtend=function(){var A=arguments;if(A.length==1){A=[this,A[0]]}for(var B in A[1]){A[0][B]=A[1][B]}return A[0]};function bkClass(){}bkClass.prototype.construct=function(){};bkClass.extend=function(C){var A=function(){if(arguments[0]!==bkClass){return this.construct.apply(this,arguments)}};var B=new this(bkClass);bkExtend(B,C);A.prototype=B;A.extend=this.extend;return A};var bkElement=bkClass.extend({construct:function(B,A){if(typeof (B)=="string"){B=(A||document).createElement(B)}B=$BK(B);return B},appendTo:function(A){A.appendChild(this);return this},appendBefore:function(A){A.parentNode.insertBefore(this,A);return this},addEvent:function(B,A){bkLib.addEvent(this,B,A);return this},setContent:function(A){this.innerHTML=A;return this},pos:function(){var C=curtop=0;var B=obj=this;if(obj.offsetParent){do{C+=obj.offsetLeft;curtop+=obj.offsetTop}while(obj=obj.offsetParent)}var A=(!window.opera)?parseInt(this.getStyle("border-width")||this.style.border)||0:0;return[C+A,curtop+A+this.offsetHeight]},noSelect:function(){bkLib.noSelect(this);return this},parentTag:function(A){var B=this;do{if(B&&B.nodeName&&B.nodeName.toUpperCase()==A){return B}B=B.parentNode}while(B);return false},hasClass:function(A){return this.className.match(new RegExp("(\\s|^)nicEdit-"+A+"(\\s|$)"))},addClass:function(A){if(!this.hasClass(A)){this.className+=" nicEdit-"+A}return this},removeClass:function(A){if(this.hasClass(A)){this.className=this.className.replace(new RegExp("(\\s|^)nicEdit-"+A+"(\\s|$)")," ")}return this},setStyle:function(A){var B=this.style;for(var C in A){switch(C){case"float":B.cssFloat=B.styleFloat=A[C];break;case"opacity":B.opacity=A[C];B.filter="alpha(opacity="+Math.round(A[C]*100)+")";break;case"className":this.className=A[C];break;default:B[C]=A[C]}}return this},getStyle:function(A,C){var B=(!C)?document.defaultView:C;if(this.nodeType==1){return(B&&B.getComputedStyle)?B.getComputedStyle(this,null).getPropertyValue(A):this.currentStyle[bkLib.camelize(A)]}},remove:function(){this.parentNode.removeChild(this);return this},setAttributes:function(A){for(var B in A){this[B]=A[B]}return this}});var bkLib={isMSIE:(navigator.appVersion.indexOf("MSIE")!=-1),addEvent:function(C,B,A){(C.addEventListener)?C.addEventListener(B,A,false):C.attachEvent("on"+B,A)},toArray:function(C){var B=C.length,A=new Array(B);while(B--){A[B]=C[B]}return A},noSelect:function(B){if(B.setAttribute&&B.nodeName.toLowerCase()!="input"&&B.nodeName.toLowerCase()!="textarea"){B.setAttribute("unselectable","on")}for(var A=0;A<B.childNodes.length;A++){bkLib.noSelect(B.childNodes[A])}},camelize:function(A){return A.replace(/\-(.)/g,function(B,C){return C.toUpperCase()})},inArray:function(A,B){return(bkLib.search(A,B)!=null)},search:function(A,C){for(var B=0;B<A.length;B++){if(A[B]==C){return B}}return null},cancelEvent:function(A){A=A||window.event;if(A.preventDefault&&A.stopPropagation){A.preventDefault();A.stopPropagation()}return false},domLoad:[],domLoaded:function(){if(arguments.callee.done){return }arguments.callee.done=true;for(i=0;i<bkLib.domLoad.length;i++){bkLib.domLoad[i]()}},onDomLoaded:function(A){this.domLoad.push(A);if(document.addEventListener){document.addEventListener("DOMContentLoaded",bkLib.domLoaded,null)}else{if(bkLib.isMSIE){document.write("<style>.nicEdit-main p { margin: 0; }</style><script id=__ie_onload defer "+((location.protocol=="https:")?"src='javascript:void(0)'":"src=//0")+"><\/script>");$BK("__ie_onload").onreadystatechange=function(){if(this.readyState=="complete"){bkLib.domLoaded()}}}}window.onload=bkLib.domLoaded}};function $BK(A){if(typeof (A)=="string"){A=document.getElementById(A)}return(A&&!A.appendTo)?bkExtend(A,bkElement.prototype):A}var bkEvent={addEvent:function(A,B){if(B){this.eventList=this.eventList||{};this.eventList[A]=this.eventList[A]||[];this.eventList[A].push(B)}return this},fireEvent:function(){var A=bkLib.toArray(arguments),C=A.shift();if(this.eventList&&this.eventList[C]){for(var B=0;B<this.eventList[C].length;B++){this.eventList[C][B].apply(this,A)}}}};function __(A){return A}Function.prototype.closure=function(){var A=this,B=bkLib.toArray(arguments),C=B.shift();return function(){if(typeof (bkLib)!="undefined"){return A.apply(C,B.concat(bkLib.toArray(arguments)))}}};Function.prototype.closureListener=function(){var A=this,C=bkLib.toArray(arguments),B=C.shift();return function(E){E=E||window.event;if(E.target){var D=E.target}else{var D=E.srcElement}return A.apply(B,[E,D].concat(C))}};
-
-
-
-var nicEditorConfig = bkClass.extend({
-	buttons : {
-		'bold' : {name : __('Click to Bold'), command : 'Bold', tags : ['B','STRONG'], css : {'font-weight' : 'bold'}, key : 'b'},
-		'italic' : {name : __('Click to Italic'), command : 'Italic', tags : ['EM','I'], css : {'font-style' : 'italic'}, key : 'i'},
-		'underline' : {name : __('Click to Underline'), command : 'Underline', tags : ['U'], css : {'text-decoration' : 'underline'}, key : 'u'},
-		'left' : {name : __('Left Align'), command : 'justifyleft', noActive : true},
-		'center' : {name : __('Center Align'), command : 'justifycenter', noActive : true},
-		'right' : {name : __('Right Align'), command : 'justifyright', noActive : true},
-		'justify' : {name : __('Justify Align'), command : 'justifyfull', noActive : true},
-		'ol' : {name : __('Insert Ordered List'), command : 'insertorderedlist', tags : ['OL']},
-		'ul' : 	{name : __('Insert Unordered List'), command : 'insertunorderedlist', tags : ['UL']},
-		'subscript' : {name : __('Click to Subscript'), command : 'subscript', tags : ['SUB']},
-		'superscript' : {name : __('Click to Superscript'), command : 'superscript', tags : ['SUP']},
-		'strikethrough' : {name : __('Click to Strike Through'), command : 'strikeThrough', css : {'text-decoration' : 'line-through'}},
-		'removeformat' : {name : __('Remove Formatting'), command : 'removeformat', noActive : true},
-		'indent' : {name : __('Indent Text'), command : 'indent', noActive : true},
-		'outdent' : {name : __('Remove Indent'), command : 'outdent', noActive : true},
-		'hr' : {name : __('Horizontal Rule'), command : 'insertHorizontalRule', noActive : true}
-	},
-	iconsPath : '../nicEditorIcons.gif',
-	buttonList : ['xhtml','save','bold','italic','underline','left','center','right','justify','ol','ul','fontSize','fontFamily','fontFormat','indent','outdent','image','upload','link','unlink','forecolor','bgcolor'],
-	iconList : {"xhtml":1,"bgcolor":2,"forecolor":3,"bold":4,"center":5,"hr":6,"indent":7,"italic":8,"justify":9,"left":10,"ol":11,"outdent":12,"removeformat":13,"right":14,"save":25,"strikethrough":16,"subscript":17,"superscript":18,"ul":19,"underline":20,"image":21,"link":22,"unlink":23,"close":24,"arrow":26}
-
-});
-;
-var nicEditors={nicPlugins:[],editors:[],registerPlugin:function(B,A){this.nicPlugins.push({p:B,o:A})},allTextAreas:function(C){var A=document.getElementsByTagName("textarea");for(var B=0;B<A.length;B++){nicEditors.editors.push(new nicEditor(C).panelInstance(A[B]))}return nicEditors.editors},findEditor:function(C){var B=nicEditors.editors;for(var A=0;A<B.length;A++){if(B[A].instanceById(C)){return B[A].instanceById(C)}}}};var nicEditor=bkClass.extend({construct:function(C){this.options=new nicEditorConfig();bkExtend(this.options,C);this.nicInstances=new Array();this.loadedPlugins=new Array();var A=nicEditors.nicPlugins;for(var B=0;B<A.length;B++){this.loadedPlugins.push(new A[B].p(this,A[B].o))}nicEditors.editors.push(this);bkLib.addEvent(document.body,"mousedown",this.selectCheck.closureListener(this))},panelInstance:function(B,C){B=this.checkReplace($BK(B));var A=new bkElement("DIV").setStyle({width:(parseInt(B.getStyle("width"))||B.clientWidth)+"px"}).appendBefore(B);this.setPanel(A);return this.addInstance(B,C)},checkReplace:function(B){var A=nicEditors.findEditor(B);if(A){A.removeInstance(B);A.removePanel()}return B},addInstance:function(B,C){B=this.checkReplace($BK(B));if(B.contentEditable||!!window.opera){var A=new nicEditorInstance(B,C,this)}else{var A=new nicEditorIFrameInstance(B,C,this)}this.nicInstances.push(A);return this},removeInstance:function(C){C=$BK(C);var B=this.nicInstances;for(var A=0;A<B.length;A++){if(B[A].e==C){B[A].remove();this.nicInstances.splice(A,1)}}},removePanel:function(A){if(this.nicPanel){this.nicPanel.remove();this.nicPanel=null}},instanceById:function(C){C=$BK(C);var B=this.nicInstances;for(var A=0;A<B.length;A++){if(B[A].e==C){return B[A]}}},setPanel:function(A){this.nicPanel=new nicEditorPanel($BK(A),this.options,this);this.fireEvent("panel",this.nicPanel);return this},nicCommand:function(B,A){if(this.selectedInstance){this.selectedInstance.nicCommand(B,A)}},getIcon:function(D,A){var C=this.options.iconList[D];var B=(A.iconFiles)?A.iconFiles[D]:"";return{backgroundImage:"url('"+((C)?this.options.iconsPath:B)+"')",backgroundPosition:((C)?((C-1)*-18):0)+"px 0px"}},selectCheck:function(C,A){var B=false;do{if(A.className&&A.className.indexOf("nicEdit")!=-1){return false}}while(A=A.parentNode);this.fireEvent("blur",this.selectedInstance,A);this.lastSelectedInstance=this.selectedInstance;this.selectedInstance=null;return false}});nicEditor=nicEditor.extend(bkEvent);
-var nicEditorInstance=bkClass.extend({isSelected:false,construct:function(G,D,C){this.ne=C;this.elm=this.e=G;this.options=D||{};newX=parseInt(G.getStyle("width"))||G.clientWidth;newY=parseInt(G.getStyle("height"))||G.clientHeight;this.initialHeight=newY-8;var H=(G.nodeName.toLowerCase()=="textarea");if(H||this.options.hasPanel){var B=(bkLib.isMSIE&&!((typeof document.body.style.maxHeight!="undefined")&&document.compatMode=="CSS1Compat"));var E={width:newX+"px",border:"1px solid #ccc",borderTop:0,overflowY:"auto",overflowX:"hidden"};E[(B)?"height":"maxHeight"]=(this.ne.options.maxHeight)?this.ne.options.maxHeight+"px":null;this.editorContain=new bkElement("DIV").setStyle(E).appendBefore(G);var A=new bkElement("DIV").setStyle({width:(newX-8)+"px",margin:"4px",minHeight:newY+"px"}).addClass("main").appendTo(this.editorContain);G.setStyle({display:"none"});A.innerHTML=G.innerHTML;if(H){A.setContent(G.value);this.copyElm=G;var F=G.parentTag("FORM");if(F){bkLib.addEvent(F,"submit",this.saveContent.closure(this))}}A.setStyle((B)?{height:newY+"px"}:{overflow:"hidden"});this.elm=A}this.ne.addEvent("blur",this.blur.closure(this));this.init();this.blur()},init:function(){this.elm.setAttribute("contentEditable","true");if(this.getContent()==""){this.setContent("<br />")}this.instanceDoc=document.defaultView;this.elm.addEvent("mousedown",this.selected.closureListener(this)).addEvent("keypress",this.keyDown.closureListener(this)).addEvent("focus",this.selected.closure(this)).addEvent("blur",this.blur.closure(this)).addEvent("keyup",this.selected.closure(this));this.ne.fireEvent("add",this)},remove:function(){this.saveContent();if(this.copyElm||this.options.hasPanel){this.editorContain.remove();this.e.setStyle({display:"block"});this.ne.removePanel()}this.disable();this.ne.fireEvent("remove",this)},disable:function(){this.elm.setAttribute("contentEditable","false")},getSel:function(){return(window.getSelection)?window.getSelection():document.selection},getRng:function(){var A=this.getSel();if(!A||A.rangeCount===0){return }return(A.rangeCount>0)?A.getRangeAt(0):A.createRange()},selRng:function(A,B){if(window.getSelection){B.removeAllRanges();B.addRange(A)}else{A.select()}},selElm:function(){var C=this.getRng();if(!C){return }if(C.startContainer){var D=C.startContainer;if(C.cloneContents().childNodes.length==1){for(var B=0;B<D.childNodes.length;B++){var A=D.childNodes[B].ownerDocument.createRange();A.selectNode(D.childNodes[B]);if(C.compareBoundaryPoints(Range.START_TO_START,A)!=1&&C.compareBoundaryPoints(Range.END_TO_END,A)!=-1){return $BK(D.childNodes[B])}}}return $BK(D)}else{return $BK((this.getSel().type=="Control")?C.item(0):C.parentElement())}},saveRng:function(){this.savedRange=this.getRng();this.savedSel=this.getSel()},restoreRng:function(){if(this.savedRange){this.selRng(this.savedRange,this.savedSel)}},keyDown:function(B,A){if(B.ctrlKey){this.ne.fireEvent("key",this,B)}},selected:function(C,A){if(!A&&!(A=this.selElm)){A=this.selElm()}if(!C.ctrlKey){var B=this.ne.selectedInstance;if(B!=this){if(B){this.ne.fireEvent("blur",B,A)}this.ne.selectedInstance=this;this.ne.fireEvent("focus",B,A)}this.ne.fireEvent("selected",B,A);this.isFocused=true;this.elm.addClass("selected")}return false},blur:function(){this.isFocused=false;this.elm.removeClass("selected")},saveContent:function(){if(this.copyElm||this.options.hasPanel){this.ne.fireEvent("save",this);(this.copyElm)?this.copyElm.value=this.getContent():this.e.innerHTML=this.getContent()}},getElm:function(){return this.elm},getContent:function(){this.content=this.getElm().innerHTML;this.ne.fireEvent("get",this);return this.content},setContent:function(A){this.content=A;this.ne.fireEvent("set",this);this.elm.innerHTML=this.content},nicCommand:function(B,A){document.execCommand(B,false,A)}});
-var nicEditorIFrameInstance=nicEditorInstance.extend({savedStyles:[],init:function(){var B=this.elm.innerHTML.replace(/^\s+|\s+$/g,"");this.elm.innerHTML="";(!B)?B="<br />":B;this.initialContent=B;this.elmFrame=new bkElement("iframe").setAttributes({src:"javascript:;",frameBorder:0,allowTransparency:"true",scrolling:"no"}).setStyle({height:"100px",width:"100%"}).addClass("frame").appendTo(this.elm);if(this.copyElm){this.elmFrame.setStyle({width:(this.elm.offsetWidth-4)+"px"})}var A=["font-size","font-family","font-weight","color"];for(itm in A){this.savedStyles[bkLib.camelize(itm)]=this.elm.getStyle(itm)}setTimeout(this.initFrame.closure(this),50)},disable:function(){this.elm.innerHTML=this.getContent()},initFrame:function(){var B=$BK(this.elmFrame.contentWindow.document);B.designMode="on";B.open();var A=this.ne.options.externalCSS;B.write("<html><head>"+((A)?'<link href="'+A+'" rel="stylesheet" type="text/css" />':"")+'</head><body id="nicEditContent" style="margin: 0 !important; background-color: transparent !important;">'+this.initialContent+"</body></html>");B.close();this.frameDoc=B;this.frameWin=$BK(this.elmFrame.contentWindow);this.frameContent=$BK(this.frameWin.document.body).setStyle(this.savedStyles);this.instanceDoc=this.frameWin.document.defaultView;this.heightUpdate();this.frameDoc.addEvent("mousedown",this.selected.closureListener(this)).addEvent("keyup",this.heightUpdate.closureListener(this)).addEvent("keydown",this.keyDown.closureListener(this)).addEvent("keyup",this.selected.closure(this));this.ne.fireEvent("add",this)},getElm:function(){return this.frameContent},setContent:function(A){this.content=A;this.ne.fireEvent("set",this);this.frameContent.innerHTML=this.content;this.heightUpdate()},getSel:function(){return(this.frameWin)?this.frameWin.getSelection():this.frameDoc.selection},heightUpdate:function(){this.elmFrame.style.height=Math.max(this.frameContent.offsetHeight,this.initialHeight)+"px"},nicCommand:function(B,A){this.frameDoc.execCommand(B,false,A);setTimeout(this.heightUpdate.closure(this),100)}});
-var nicEditorPanel=bkClass.extend({construct:function(E,B,A){this.elm=E;this.options=B;this.ne=A;this.panelButtons=new Array();this.buttonList=bkExtend([],this.ne.options.buttonList);this.panelContain=new bkElement("DIV").setStyle({overflow:"hidden",width:"100%",border:"1px solid #cccccc",backgroundColor:"#efefef"}).addClass("panelContain");this.panelElm=new bkElement("DIV").setStyle({margin:"2px",marginTop:"0px",zoom:1,overflow:"hidden"}).addClass("panel").appendTo(this.panelContain);this.panelContain.appendTo(E);var C=this.ne.options;var D=C.buttons;for(button in D){this.addButton(button,C,true)}this.reorder();E.noSelect()},addButton:function(buttonName,options,noOrder){var button=options.buttons[buttonName];var type=(button.type)?eval("(typeof("+button.type+') == "undefined") ? null : '+button.type+";"):nicEditorButton;var hasButton=bkLib.inArray(this.buttonList,buttonName);if(type&&(hasButton||this.ne.options.fullPanel)){this.panelButtons.push(new type(this.panelElm,buttonName,options,this.ne));if(!hasButton){this.buttonList.push(buttonName)}}},findButton:function(B){for(var A=0;A<this.panelButtons.length;A++){if(this.panelButtons[A].name==B){return this.panelButtons[A]}}},reorder:function(){var C=this.buttonList;for(var B=0;B<C.length;B++){var A=this.findButton(C[B]);if(A){this.panelElm.appendChild(A.margin)}}},remove:function(){this.elm.remove()}});
-var nicEditorButton=bkClass.extend({construct:function(D,A,C,B){this.options=C.buttons[A];this.name=A;this.ne=B;this.elm=D;this.margin=new bkElement("DIV").setStyle({"float":"left",marginTop:"2px"}).appendTo(D);this.contain=new bkElement("DIV").setStyle({width:"20px",height:"20px"}).addClass("buttonContain").appendTo(this.margin);this.border=new bkElement("DIV").setStyle({backgroundColor:"#efefef",border:"1px solid #efefef"}).appendTo(this.contain);this.button=new bkElement("DIV").setStyle({width:"18px",height:"18px",overflow:"hidden",zoom:1,cursor:"pointer"}).addClass("button").setStyle(this.ne.getIcon(A,C)).appendTo(this.border);this.button.addEvent("mouseover",this.hoverOn.closure(this)).addEvent("mouseout",this.hoverOff.closure(this)).addEvent("mousedown",this.mouseClick.closure(this)).noSelect();if(!window.opera){this.button.onmousedown=this.button.onclick=bkLib.cancelEvent}B.addEvent("selected",this.enable.closure(this)).addEvent("blur",this.disable.closure(this)).addEvent("key",this.key.closure(this));this.disable();this.init()},init:function(){},hide:function(){this.contain.setStyle({display:"none"})},updateState:function(){if(this.isDisabled){this.setBg()}else{if(this.isHover){this.setBg("hover")}else{if(this.isActive){this.setBg("active")}else{this.setBg()}}}},setBg:function(A){switch(A){case"hover":var B={border:"1px solid #666",backgroundColor:"#ddd"};break;case"active":var B={border:"1px solid #666",backgroundColor:"#ccc"};break;default:var B={border:"1px solid #efefef",backgroundColor:"#efefef"}}this.border.setStyle(B).addClass("button-"+A)},checkNodes:function(A){var B=A;do{if(this.options.tags&&bkLib.inArray(this.options.tags,B.nodeName)){this.activate();return true}}while(B=B.parentNode&&B.className!="nicEdit");B=$BK(A);while(B.nodeType==3){B=$BK(B.parentNode)}if(this.options.css){for(itm in this.options.css){if(B.getStyle(itm,this.ne.selectedInstance.instanceDoc)==this.options.css[itm]){this.activate();return true}}}this.deactivate();return false},activate:function(){if(!this.isDisabled){this.isActive=true;this.updateState();this.ne.fireEvent("buttonActivate",this)}},deactivate:function(){this.isActive=false;this.updateState();if(!this.isDisabled){this.ne.fireEvent("buttonDeactivate",this)}},enable:function(A,B){this.isDisabled=false;this.contain.setStyle({opacity:1}).addClass("buttonEnabled");this.updateState();this.checkNodes(B)},disable:function(A,B){this.isDisabled=true;this.contain.setStyle({opacity:0.6}).removeClass("buttonEnabled");this.updateState()},toggleActive:function(){(this.isActive)?this.deactivate():this.activate()},hoverOn:function(){if(!this.isDisabled){this.isHover=true;this.updateState();this.ne.fireEvent("buttonOver",this)}},hoverOff:function(){this.isHover=false;this.updateState();this.ne.fireEvent("buttonOut",this)},mouseClick:function(){if(this.options.command){this.ne.nicCommand(this.options.command,this.options.commandArgs);if(!this.options.noActive){this.toggleActive()}}this.ne.fireEvent("buttonClick",this)},key:function(A,B){if(this.options.key&&B.ctrlKey&&String.fromCharCode(B.keyCode||B.charCode).toLowerCase()==this.options.key){this.mouseClick();if(B.preventDefault){B.preventDefault()}}}});
-var nicPlugin=bkClass.extend({construct:function(B,A){this.options=A;this.ne=B;this.ne.addEvent("panel",this.loadPanel.closure(this));this.init()},loadPanel:function(C){var B=this.options.buttons;for(var A in B){C.addButton(A,this.options)}C.reorder()},init:function(){}});
-
-
-var nicPaneOptions = { };
-
-var nicEditorPane=bkClass.extend({construct:function(D,C,B,A){this.ne=C;this.elm=D;this.pos=D.pos();this.contain=new bkElement("div").setStyle({zIndex:"99999",overflow:"hidden",position:"absolute",left:this.pos[0]+"px",top:this.pos[1]+"px"});this.pane=new bkElement("div").setStyle({fontSize:"12px",border:"1px solid #ccc",overflow:"hidden",padding:"4px",textAlign:"left",backgroundColor:"#ffffc9"}).addClass("pane").setStyle(B).appendTo(this.contain);if(A&&!A.options.noClose){this.close=new bkElement("div").setStyle({"float":"right",height:"16px",width:"16px",cursor:"pointer"}).setStyle(this.ne.getIcon("close",nicPaneOptions)).addEvent("mousedown",A.removePane.closure(this)).appendTo(this.pane)}this.contain.noSelect().appendTo(document.body);this.position();this.init()},init:function(){},position:function(){if(this.ne.nicPanel){var B=this.ne.nicPanel.elm;var A=B.pos();var C=A[0]+parseInt(B.getStyle("width"))-(parseInt(this.pane.getStyle("width"))+8);if(C<this.pos[0]){this.contain.setStyle({left:C+"px"})}}},toggle:function(){this.isVisible=!this.isVisible;this.contain.setStyle({display:((this.isVisible)?"block":"none")})},remove:function(){if(this.contain){this.contain.remove();this.contain=null}},append:function(A){A.appendTo(this.pane)},setContent:function(A){this.pane.setContent(A)}});
-
-var nicEditorAdvancedButton=nicEditorButton.extend({init:function(){this.ne.addEvent("selected",this.removePane.closure(this)).addEvent("blur",this.removePane.closure(this))},mouseClick:function(){if(!this.isDisabled){if(this.pane&&this.pane.pane){this.removePane()}else{this.pane=new nicEditorPane(this.contain,this.ne,{width:(this.width||"270px"),backgroundColor:"#fff"},this);this.addPane();this.ne.selectedInstance.saveRng()}}},addForm:function(C,G){this.form=new bkElement("form").addEvent("submit",this.submit.closureListener(this));this.pane.append(this.form);this.inputs={};for(itm in C){var D=C[itm];var F="";if(G){F=G.getAttribute(itm)}if(!F){F=D.value||""}var A=C[itm].type;if(A=="title"){new bkElement("div").setContent(D.txt).setStyle({fontSize:"14px",fontWeight:"bold",padding:"0px",margin:"2px 0"}).appendTo(this.form)}else{var B=new bkElement("div").setStyle({overflow:"hidden",clear:"both"}).appendTo(this.form);if(D.txt){new bkElement("label").setAttributes({"for":itm}).setContent(D.txt).setStyle({margin:"2px 4px",fontSize:"13px",width:"50px",lineHeight:"20px",textAlign:"right","float":"left"}).appendTo(B)}switch(A){case"text":this.inputs[itm]=new bkElement("input").setAttributes({id:itm,value:F,type:"text"}).setStyle({margin:"2px 0",fontSize:"13px","float":"left",height:"20px",border:"1px solid #ccc",overflow:"hidden"}).setStyle(D.style).appendTo(B);break;case"select":this.inputs[itm]=new bkElement("select").setAttributes({id:itm}).setStyle({border:"1px solid #ccc","float":"left",margin:"2px 0"}).appendTo(B);for(opt in D.options){var E=new bkElement("option").setAttributes({value:opt,selected:(opt==F)?"selected":""}).setContent(D.options[opt]).appendTo(this.inputs[itm])}break;case"content":this.inputs[itm]=new bkElement("textarea").setAttributes({id:itm}).setStyle({border:"1px solid #ccc","float":"left"}).setStyle(D.style).appendTo(B);this.inputs[itm].value=F}}}new bkElement("input").setAttributes({type:"submit"}).setStyle({backgroundColor:"#efefef",border:"1px solid #ccc",margin:"3px 0","float":"left",clear:"both"}).appendTo(this.form);this.form.onsubmit=bkLib.cancelEvent},submit:function(){},findElm:function(B,A,E){var D=this.ne.selectedInstance.getElm().getElementsByTagName(B);for(var C=0;C<D.length;C++){if(D[C].getAttribute(A)==E){return $BK(D[C])}}},removePane:function(){if(this.pane){this.pane.remove();this.pane=null;this.ne.selectedInstance.restoreRng()}}});
-
-var nicButtonTips=bkClass.extend({construct:function(A){this.ne=A;A.addEvent("buttonOver",this.show.closure(this)).addEvent("buttonOut",this.hide.closure(this))},show:function(A){this.timer=setTimeout(this.create.closure(this,A),400)},create:function(A){this.timer=null;if(!this.pane){this.pane=new nicEditorPane(A.button,this.ne,{fontSize:"12px",marginTop:"5px"});this.pane.setContent(A.options.name)}},hide:function(A){if(this.timer){clearTimeout(this.timer)}if(this.pane){this.pane=this.pane.remove()}}});nicEditors.registerPlugin(nicButtonTips);
-
-
-var nicSelectOptions = {
-	buttons : {
-		'fontSize' : {name : __('Select Font Size'), type : 'nicEditorFontSizeSelect', command : 'fontsize'},
-		'fontFamily' : {name : __('Select Font Family'), type : 'nicEditorFontFamilySelect', command : 'fontname'},
-		'fontFormat' : {name : __('Select Font Format'), type : 'nicEditorFontFormatSelect', command : 'formatBlock'}
-	}
-};
-
-var nicEditorSelect=bkClass.extend({construct:function(D,A,C,B){this.options=C.buttons[A];this.elm=D;this.ne=B;this.name=A;this.selOptions=new Array();this.margin=new bkElement("div").setStyle({"float":"left",margin:"2px 1px 0 1px"}).appendTo(this.elm);this.contain=new bkElement("div").setStyle({width:"90px",height:"20px",cursor:"pointer",overflow:"hidden"}).addClass("selectContain").addEvent("click",this.toggle.closure(this)).appendTo(this.margin);this.items=new bkElement("div").setStyle({overflow:"hidden",zoom:1,border:"1px solid #ccc",paddingLeft:"3px",backgroundColor:"#fff"}).appendTo(this.contain);this.control=new bkElement("div").setStyle({overflow:"hidden","float":"right",height:"18px",width:"16px"}).addClass("selectControl").setStyle(this.ne.getIcon("arrow",C)).appendTo(this.items);this.txt=new bkElement("div").setStyle({overflow:"hidden","float":"left",width:"66px",height:"14px",marginTop:"1px",fontFamily:"sans-serif",textAlign:"center",fontSize:"12px"}).addClass("selectTxt").appendTo(this.items);if(!window.opera){this.contain.onmousedown=this.control.onmousedown=this.txt.onmousedown=bkLib.cancelEvent}this.margin.noSelect();this.ne.addEvent("selected",this.enable.closure(this)).addEvent("blur",this.disable.closure(this));this.disable();this.init()},disable:function(){this.isDisabled=true;this.close();this.contain.setStyle({opacity:0.6})},enable:function(A){this.isDisabled=false;this.close();this.contain.setStyle({opacity:1})},setDisplay:function(A){this.txt.setContent(A)},toggle:function(){if(!this.isDisabled){(this.pane)?this.close():this.open()}},open:function(){this.pane=new nicEditorPane(this.items,this.ne,{width:"88px",padding:"0px",borderTop:0,borderLeft:"1px solid #ccc",borderRight:"1px solid #ccc",borderBottom:"0px",backgroundColor:"#fff"});for(var C=0;C<this.selOptions.length;C++){var B=this.selOptions[C];var A=new bkElement("div").setStyle({overflow:"hidden",borderBottom:"1px solid #ccc",width:"88px",textAlign:"left",overflow:"hidden",cursor:"pointer"});var D=new bkElement("div").setStyle({padding:"0px 4px"}).setContent(B[1]).appendTo(A).noSelect();D.addEvent("click",this.update.closure(this,B[0])).addEvent("mouseover",this.over.closure(this,D)).addEvent("mouseout",this.out.closure(this,D)).setAttributes("id",B[0]);this.pane.append(A);if(!window.opera){D.onmousedown=bkLib.cancelEvent}}},close:function(){if(this.pane){this.pane=this.pane.remove()}},over:function(A){A.setStyle({backgroundColor:"#ccc"})},out:function(A){A.setStyle({backgroundColor:"#fff"})},add:function(B,A){this.selOptions.push(new Array(B,A))},update:function(A){this.ne.nicCommand(this.options.command,A);this.close()}});var nicEditorFontSizeSelect=nicEditorSelect.extend({sel:{1:"1&nbsp;(8pt)",2:"2&nbsp;(10pt)",3:"3&nbsp;(12pt)",4:"4&nbsp;(14pt)",5:"5&nbsp;(18pt)",6:"6&nbsp;(24pt)"},init:function(){this.setDisplay("Font&nbsp;Size...");for(itm in this.sel){this.add(itm,'<font size="'+itm+'">'+this.sel[itm]+"</font>")}}});var nicEditorFontFamilySelect=nicEditorSelect.extend({sel:{arial:"Arial","comic sans ms":"Comic Sans","courier new":"Courier New",georgia:"Georgia",helvetica:"Helvetica",impact:"Impact","times new roman":"Times","trebuchet ms":"Trebuchet",verdana:"Verdana"},init:function(){this.setDisplay("Font&nbsp;Family...");for(itm in this.sel){this.add(itm,'<font face="'+itm+'">'+this.sel[itm]+"</font>")}}});var nicEditorFontFormatSelect=nicEditorSelect.extend({sel:{p:"Paragraph",pre:"Pre",h6:"Heading&nbsp;6",h5:"Heading&nbsp;5",h4:"Heading&nbsp;4",h3:"Heading&nbsp;3",h2:"Heading&nbsp;2",h1:"Heading&nbsp;1"},init:function(){this.setDisplay("Font&nbsp;Format...");for(itm in this.sel){var A=itm.toUpperCase();this.add("<"+A+">","<"+itm+' style="padding: 0px; margin: 0px;">'+this.sel[itm]+"</"+A+">")}}});nicEditors.registerPlugin(nicPlugin,nicSelectOptions);
-
-
-var nicLinkOptions = {
-	buttons : {
-		'link' : {name : 'Add Link', type : 'nicLinkButton', tags : ['A']},
-		'unlink' : {name : 'Remove Link',  command : 'unlink', noActive : true}
-	}
-};
-
-var nicLinkButton=nicEditorAdvancedButton.extend({addPane:function(){this.ln=this.ne.selectedInstance.selElm().parentTag("A");this.addForm({"":{type:"title",txt:"Add/Edit Link"},href:{type:"text",txt:"URL",value:"http://",style:{width:"150px"}},title:{type:"text",txt:"Title"},target:{type:"select",txt:"Open In",options:{"":"Current Window",_blank:"New Window"},style:{width:"100px"}}},this.ln)},submit:function(C){var A=this.inputs.href.value;if(A=="http://"||A==""){alert("You must enter a URL to Create a Link");return false}this.removePane();if(!this.ln){var B="javascript:nicTemp();";this.ne.nicCommand("createlink",B);this.ln=this.findElm("A","href",B)}if(this.ln){this.ln.setAttributes({href:this.inputs.href.value,title:this.inputs.title.value,target:this.inputs.target.options[this.inputs.target.selectedIndex].value})}}});nicEditors.registerPlugin(nicPlugin,nicLinkOptions);
-
-
-var nicColorOptions = {
-	buttons : {
-		'forecolor' : {name : __('Change Text Color'), type : 'nicEditorColorButton', noClose : true},
-		'bgcolor' : {name : __('Change Background Color'), type : 'nicEditorBgColorButton', noClose : true}
-	}
-};
-
-var nicEditorColorButton=nicEditorAdvancedButton.extend({addPane:function(){var D={0:"00",1:"33",2:"66",3:"99",4:"CC",5:"FF"};var H=new bkElement("DIV").setStyle({width:"270px"});for(var A in D){for(var F in D){for(var E in D){var I="#"+D[A]+D[E]+D[F];var C=new bkElement("DIV").setStyle({cursor:"pointer",height:"15px","float":"left"}).appendTo(H);var G=new bkElement("DIV").setStyle({border:"2px solid "+I}).appendTo(C);var B=new bkElement("DIV").setStyle({backgroundColor:I,overflow:"hidden",width:"11px",height:"11px"}).addEvent("click",this.colorSelect.closure(this,I)).addEvent("mouseover",this.on.closure(this,G)).addEvent("mouseout",this.off.closure(this,G,I)).appendTo(G);if(!window.opera){C.onmousedown=B.onmousedown=bkLib.cancelEvent}}}}this.pane.append(H.noSelect())},colorSelect:function(A){this.ne.nicCommand("foreColor",A);this.removePane()},on:function(A){A.setStyle({border:"2px solid #000"})},off:function(A,B){A.setStyle({border:"2px solid "+B})}});var nicEditorBgColorButton=nicEditorColorButton.extend({colorSelect:function(A){this.ne.nicCommand("hiliteColor",A);this.removePane()}});nicEditors.registerPlugin(nicPlugin,nicColorOptions);
-
-
-var nicImageOptions = {
-	buttons : {
-		'image' : {name : 'Add Image', type : 'nicImageButton', tags : ['IMG']}
-	}
-
-};
-
-var nicImageButton=nicEditorAdvancedButton.extend({addPane:function(){this.im=this.ne.selectedInstance.selElm().parentTag("IMG");this.addForm({"":{type:"title",txt:"Add/Edit Image"},src:{type:"text",txt:"URL",value:"http://",style:{width:"150px"}},alt:{type:"text",txt:"Alt Text",style:{width:"100px"}},align:{type:"select",txt:"Align",options:{none:"Default",left:"Left",right:"Right"}}},this.im)},submit:function(B){var C=this.inputs.src.value;if(C==""||C=="http://"){alert("You must enter a Image URL to insert");return false}this.removePane();if(!this.im){var A="javascript:nicImTemp();";this.ne.nicCommand("insertImage",A);this.im=this.findElm("IMG","src",A)}if(this.im){this.im.setAttributes({src:this.inputs.src.value,alt:this.inputs.alt.value,align:this.inputs.align.value})}}});nicEditors.registerPlugin(nicPlugin,nicImageOptions);
-
-
-var nicSaveOptions = {
-	buttons : {
-		'save' : {name : __('Save this content'), type : 'nicEditorSaveButton'}
-	}
-};
-
-var nicEditorSaveButton=nicEditorButton.extend({init:function(){if(!this.ne.options.onSave){this.margin.setStyle({display:"none"})}},mouseClick:function(){var B=this.ne.options.onSave;var A=this.ne.selectedInstance;B(A.getContent(),A.elm.id,A)}});nicEditors.registerPlugin(nicPlugin,nicSaveOptions);
-
-
-var nicCodeOptions = {
-	buttons : {
-		'xhtml' : {name : 'Edit HTML', type : 'nicCodeButton'}
-	}
-
-};
-
-var nicCodeButton=nicEditorAdvancedButton.extend({width:"750px",addPane:function(){this.addForm({"":{type:"title",txt:"Edit HTML"},code:{type:"content",value:this.ne.selectedInstance.getContent(),style:{width:"740px",height:"200px"}}})},submit:function(B){var A=this.inputs.code.value;this.ne.selectedInstance.setContent(A);this.removePane()}});nicEditors.registerPlugin(nicPlugin,nicCodeOptions);
-
diff --git a/scp/js/scp.js b/scp/js/scp.js
index d79b03955150d2221fe8a3ef920c1a021db5b301..d1493f59036b5e762cdf3ccfefa02e96c35e573b 100644
--- a/scp/js/scp.js
+++ b/scp/js/scp.js
@@ -13,19 +13,25 @@ function checkbox_checker(formObj, min, max) {
     var checked=$('input:checkbox:checked', formObj).length;
     var action= action?action:"process";
     if (max>0 && checked > max ){
-        msg="You're limited to only " + max + " selections.\n"
-        msg=msg + "You have made " + checked + " selections.\n"
-        msg=msg + "Please remove " + (checked-max) + " selection(s)."
-        alert(msg)
+        msg=__("You're limited to only {0} selections.\n") .replace('{0}', max);
+        msg=msg + __("You have made {0} selections.\n").replace('{0}', checked);
+        msg=msg + __("Please remove {0} selection(s).").replace('{0}', checked-max);
+        $.sysAlert(__('Alert'), msg);
+
         return (false);
     }
 
     if (checked< min ){
-        alert("Please make at least " + min + " selections. " + checked + " checked so far.")
+        $.sysAlert( __('Alert'),
+                __("Please make at least {0} selections. {1} checked so far.")
+                .replace('{0}', min)
+                .replace('{1}', checked)
+                );
+
         return (false);
     }
 
-    return (true);
+    return checked;
 }
 
 
@@ -87,12 +93,12 @@ var scp_prep = function() {
             $('.dialog#confirm-action').delegate('input.confirm', 'click.confirm', function(e) {
                 e.preventDefault();
                 $('.dialog#confirm-action').hide();
-                $('#overlay').hide();
+                $.toggleOverlay(false);
                 $('input#action', formObj).val(action);
                 formObj.submit();
                 return false;
              });
-            $('#overlay').show();
+            $.toggleOverlay(true);
             $('.dialog#confirm-action .confirm-action').hide();
             $('.dialog#confirm-action p#'+this.name+'-confirm')
             .show()
@@ -109,7 +115,7 @@ var scp_prep = function() {
             var action = $(this).attr('href').substr(1, $(this).attr('href').length);
 
             $('input#action', $dialog).val(action);
-            $('#overlay').show();
+            $.toggleOverlay(true);
             $('.confirm-action', $dialog).hide();
             $('p'+$(this).attr('href')+'-confirm', $dialog)
             .show()
@@ -138,10 +144,10 @@ var scp_prep = function() {
             fObj.data('changed', true);
             $('input[type=submit]', fObj).css('color', 'red');
             $(window).bind('beforeunload', function(e) {
-                return 'Are you sure you want to leave? Any changes or info you\'ve entered will be discarded!';
+                return __('Are you sure you want to leave? Any changes or info you\'ve entered will be discarded!');
             });
             $(document).on('pjax:beforeSend.changed', function(e) {
-                return confirm('Are you sure you want to leave? Any changes or info you\'ve entered will be discarded!');
+                return confirm(__('Are you sure you want to leave? Any changes or info you\'ve entered will be discarded!'));
             });
         }
     };
@@ -185,7 +191,7 @@ var scp_prep = function() {
     //Canned attachments.
     $('.canned_attachments, .faq_attachments').delegate('input:checkbox', 'click', function(e) {
         var elem = $(this);
-        if(!$(this).is(':checked') && confirm("Are you sure you want to remove this attachment?")==true) {
+        if(!$(this).is(':checked') && confirm(__("Are you sure you want to remove this attachment?"))==true) {
             elem.parent().addClass('strike');
         } else {
             elem.attr('checked', 'checked');
@@ -223,16 +229,12 @@ var scp_prep = function() {
                             redactor.observeStart();
                     }
                     //Canned attachments.
-                    if(canned.files && $('.canned_attachments',fObj).length) {
+                    var ca = $('.attachments', fObj);
+                    if(canned.files && ca.length) {
+                        var fdb = ca.find('.dropzone').data('dropbox');
                         $.each(canned.files,function(i, j) {
-                            if(!$('.canned_attachments #f'+j.id,fObj).length) {
-                                var file='<span><label><input type="checkbox" name="cannedattachments[]" value="' + j.id+'" id="f'+j.id+'" checked="checked">';
-                                    file+= ' '+ j.name + '</label>';
-                                    file+= ' (<a target="_blank" class="no-pjax" href="file.php?h=' + j.key + j.hash + '">view</a>) </span>';
-                                $('.canned_attachments', fObj).append(file);
-                            }
-
-                         });
+                          fdb.addNode(j);
+                        });
                     }
                 }
             })
@@ -243,14 +245,6 @@ var scp_prep = function() {
 
     /* Get config settings from the backend */
     getConfig().then(function(c) {
-        // Multifile uploads
-        $('.multifile').multifile({
-            container:   '.uploads',
-            max_uploads: c.max_file_uploads || 1,
-            file_types:  c.file_types || ".*",
-            max_file_size: c.max_file_size || 0
-        });
-
         // Datepicker
         $('.dp').datepicker({
             numberOfMonths: 2,
@@ -339,7 +333,7 @@ var scp_prep = function() {
     $('.dialog').delegate('input.close, a.close', 'click', function(e) {
         e.preventDefault();
         $(this).parents('div.dialog').hide()
-        $('#overlay').hide();
+        $.toggleOverlay(false);
 
         return false;
     });
@@ -359,28 +353,36 @@ var scp_prep = function() {
     $('#go-advanced').click(function(e) {
         e.preventDefault();
         $('#result-count').html('');
-        $('#overlay').show();
+        $.toggleOverlay(true);
         $('#advanced-search').show();
     });
 
 
-    $('#advanced-search').delegate('#status', 'change', function() {
-        switch($(this).val()) {
+    $('#advanced-search').delegate('#statusId, #flag', 'change', function() {
+        switch($(this).children('option:selected').data('state')) {
             case 'closed':
-                $('select#assignee').find('option:first').attr('selected', 'selected').parent('select');
-                $('select#assignee').attr('disabled','disabled');
+                $('select#assignee')
+                .attr('disabled','disabled')
+                .find('option:first')
+                .attr('selected', 'selected');
+                $('select#flag')
+                .attr('disabled','disabled')
+                .find('option:first')
+                .attr('selected', 'selected');
                 $('select#staffId').removeAttr('disabled');
                 break;
             case 'open':
-            case 'overdue':
-            case 'answered':
-                $('select#staffId').find('option:first').attr('selected', 'selected').parent('select');
-                $('select#staffId').attr('disabled','disabled');
+                $('select#staffId')
+                .attr('disabled','disabled')
+                .find('option:first')
+                .attr('selected', 'selected');
                 $('select#assignee').removeAttr('disabled');
+                $('select#flag').removeAttr('disabled');
                 break;
             default:
                 $('select#staffId').removeAttr('disabled');
                 $('select#assignee').removeAttr('disabled');
+                $('select#flag').removeAttr('disabled');
         }
     });
 
@@ -412,7 +414,8 @@ var scp_prep = function() {
             .done( function () {
              })
             .fail( function () {
-                $('#result-count').html('<div class="fail">Advanced search failed - try again!</div>');
+                $('#result-count').html('<div class="fail">'
+                    + __('Advanced search failed - try again!') + '</div>');
             })
             .always( function () {
                 $('.spinner', elem).hide();
@@ -509,7 +512,7 @@ $(document).keydown(function(e) {
 
     if (e.keyCode == 27 && !$('#overlay').is(':hidden')) {
         $('div.dialog').hide();
-        $('#overlay').hide();
+        $.toggleOverlay(false);
 
         e.preventDefault();
         e.stopPropagation();
@@ -517,23 +520,46 @@ $(document).keydown(function(e) {
     }
 });
 
+$.toggleOverlay = function (show) {
+  if (typeof(show) === 'undefined') {
+    return $.toggleOverlay(!$('#overlay').is(':visible'));
+  }
+  if (show) {
+    $('#overlay').fadeIn();
+    $('body').css('overflow', 'hidden');
+  }
+  else {
+    $('#overlay').fadeOut();
+    $('body').css('overflow', 'auto');
+  }
+};
+
 $.dialog = function (url, codes, cb, options) {
     options = options||{};
 
     if (codes && !$.isArray(codes))
         codes = [codes];
 
-    $('.dialog#popup .body').load(url, function () {
-        $('#overlay').show();
-        $('.dialog#popup').show({
-            duration: 0,
+    var $popup = $('.dialog#popup');
+
+    $.toggleOverlay(true);
+    $('div.body', $popup).empty().hide();
+    $('div#popup-loading', $popup).show()
+        .find('h1').css({'margin-top':function() { return $popup.height()/3-$(this).height()/3}});
+    $popup.show();
+    $('div.body', $popup).load(url, function () {
+        $('div#popup-loading', $popup).hide();
+        $('div.body', $popup).slideDown({
+            duration: 300,
+            queue: false,
             complete: function() { if (options.onshow) options.onshow(); }
         });
         $(document).off('.dialog');
         $(document).on('submit.dialog', '.dialog#popup form', function(e) {
             e.preventDefault();
             var $form = $(this);
-            var $dialog = $form.closest('.dialog');
+            $('div#popup-loading', $popup).show()
+                .find('h1').css({'margin-top':function() { return $popup.height()/3-$(this).height()/3}});
             $.ajax({
                 type:  $form.attr('method'),
                 url: 'ajax.php/'+$form.attr('action').substr(1),
@@ -542,17 +568,20 @@ $.dialog = function (url, codes, cb, options) {
                 success: function(resp, status, xhr) {
                     if (xhr && xhr.status && codes
                         && $.inArray(xhr.status, codes) != -1) {
-                        $('div.body', $dialog).empty();
-                        $dialog.hide();
-                        $('#overlay').hide();
+                        $.toggleOverlay(false);
+                        $popup.hide();
+                        $('div.body', $popup).empty();
                         if(cb) cb(xhr);
                     } else {
-                        $('div.body', $dialog).html(resp);
-                        $('#msg_notice, #msg_error', $dialog).delay(5000).slideUp();
+                        $('div.body', $popup).html(resp);
+                        $popup.effect('shake');
+                        $('#msg_notice, #msg_error', $popup).delay(5000).slideUp();
                     }
                 }
             })
-            .done(function() { })
+            .done(function() {
+                $('div#popup-loading', $popup).hide();
+            })
             .fail(function() { });
             return false;
         });
@@ -560,6 +589,18 @@ $.dialog = function (url, codes, cb, options) {
     if (options.onload) { options.onload(); }
  };
 
+$.sysAlert = function (title, msg, cb) {
+    var $dialog =  $('.dialog#alert');
+    if ($dialog.length) {
+        $.toggleOverlay(true);
+        $('#title', $dialog).html(title);
+        $('#body', $dialog).html(msg);
+        $dialog.show();
+    } else {
+        alert(msg);
+    }
+};
+
 $.userLookup = function (url, cb) {
     $.dialog(url, 201, function (xhr) {
         var user = $.parseJSON(xhr.responseText);
@@ -578,6 +619,8 @@ $.orgLookup = function (url, cb) {
     });
 };
 
+$.uid = 1;
+
 //Tabs
 $(document).on('click.tab', 'ul.tabs li a', function(e) {
     e.preventDefault();
@@ -644,6 +687,9 @@ $(document).on('pjax:start', function() {
     // Cancel save-changes warning banner
     $(document).unbind('pjax:beforeSend.changed');
     $(window).unbind('beforeunload');
+    // Close popups
+    $('.dialog .body').empty().parent().hide();
+    // Close tooltips
     $('.tip_box').remove();
 });
 
@@ -666,18 +712,8 @@ $(document).on('pjax:complete', function() {
     $("#loadingbar").width("101%").delay(200).fadeOut(400, function() {
         $(this).remove();
     });
-
-    $('.tip_box').remove();
-    $('.dialog .body').empty().parent().hide();
-    $('#overlay').stop(false, true).hide().removeAttr('style');
-});
-
-$(document).on('pjax:end', function() {
-    // Close popups
-    // Close tooltips
-    $('.tip_box').remove();
-    $('.dialog .body').empty().parent().hide();
-    $('#overlay').hide();
+    $.toggleOverlay(false);
+    $('#overlay').removeAttr('style');
 });
 
 // Quick note interface
@@ -745,7 +781,7 @@ $('.quicknote .delete').live('click.note', function() {
 $('#new-note').live('click', function() {
   var note = $(this).closest('.quicknote'),
     T = $('<textarea>'),
-    button = $('<input type="button">').val('Create');
+    button = $('<input type="button">').val(__('Create'));
     button.click(function() {
       $.post('ajax.php/' + note.data('url'),
         { note: T.redactor('get'), no_options: note.hasClass('no-options') },
@@ -766,3 +802,9 @@ $('#new-note').live('click', function() {
     $(T).redactor('focus');
     return false;
 });
+
+function __(s) {
+  if ($.oststrings && $.oststrings[s])
+    return $.oststrings[s];
+  return s;
+}
diff --git a/scp/js/ticket.js b/scp/js/ticket.js
index e6590602819eeb277f9faba7abd1034ea3217da9..2c9398a5ac28bf56b2f8da4ba0c02f927d97fc49 100644
--- a/scp/js/ticket.js
+++ b/scp/js/ticket.js
@@ -52,10 +52,10 @@ var autoLock = {
 
         if(!autoLock.lasteventTime) { //I hate nav away warnings..but
             $(document).on('pjax:beforeSend.changed', function(e) {
-                return confirm("Any changes or info you've entered will be discarded!");
+                return confirm(__("Any changes or info you've entered will be discarded!"));
             });
             $(window).bind('beforeunload', function(e) {
-                return "Any changes or info you've entered will be discarded!";
+                return __("Any changes or info you've entered will be discarded!");
              });
         }
 
@@ -135,8 +135,7 @@ var autoLock = {
             $(window).unbind('beforeunload');
             //Only warn if we had a failed lock attempt.
             if(autoLock.warn && !autoLock.lockId && autoLock.lasteventTime) {
-                var answer=confirm('Unable to acquire a lock on the ticket. Someone else could be working on the same ticket. \
-                    Please confirm if you wish to continue anyways.');
+                var answer=confirm(__('Unable to acquire a lock on the ticket. Someone else could be working on the same ticket.  Please confirm if you wish to continue anyways.'));
                 if(!answer) {
                     e.returnValue=false;
                     e.cancelBubble=true;
@@ -236,7 +235,7 @@ var autoLock = {
                     autoLock.lockAttempts++;
                     if(warn && (!lock.retry || autoLock.lockAttempts>=autoLock.maxattempts)) {
                         autoLock.retry=false;
-                        alert('Unable to lock the ticket. Someone else could be working on the same ticket.');
+                        alert(__('Unable to lock the ticket. Someone else could be working on the same ticket.'));
                     }
                 }
                 break;
@@ -244,7 +243,7 @@ var autoLock = {
     },
 
     discardWarning: function(e) {
-        e.returnValue="Any changes or info you've entered will be discarded!";
+        e.returnValue=__("Any changes or info you've entered will be discarded!");
     },
 
     //TODO: Monitor events and elapsed time and warn user when the lock is about to expire.
@@ -307,7 +306,7 @@ $.showImagesInline = function(urls, thread_id) {
                     }
                 ).append($('<div class="caption">')
                     .append('<span class="filename">'+info.filename+'</span>')
-                    .append('<a href="'+info.download_url+'" class="action-button no-pjax"><i class="icon-download-alt"></i> Download</a>')
+                    .append('<a href="'+info.download_url+'" class="action-button pull-right no-pjax"><i class="icon-download-alt"></i> '+__('Download')+'</a>')
                 );
             e.data('wrapped', true);
         }
@@ -382,7 +381,7 @@ var ticket_onload = function($) {
     autoLock.Init();
 
     /*** Ticket Actions **/
-    //print options
+    //print options TODO: move to backend
     $('a#ticket-print').click(function(e) {
         e.preventDefault();
         $('#overlay').show();
@@ -390,11 +389,18 @@ var ticket_onload = function($) {
         return false;
     });
 
-    //ticket status (close & reopen) xxx: move to backend ticket-action
-    $('a#ticket-close, a#ticket-reopen').click(function(e) {
+
+    $(document).off('.ticket-action');
+    $(document).on('click.ticket-action', 'a.ticket-action', function(e) {
         e.preventDefault();
-        $('#overlay').show();
-        $('.dialog#ticket-status').show();
+        var url = 'ajax.php/'
+        +$(this).attr('href').substr(1)
+        +'?_uid='+new Date().getTime();
+        var $redirect = $(this).data('href');
+        $.dialog(url, [201], function (xhr) {
+            window.location.href = $redirect ? $redirect : window.location.href;
+        });
+
         return false;
     });
 
@@ -413,9 +419,9 @@ var ticket_onload = function($) {
         if (!extra) return;
         if (!imgs.length) return;
         extra.append($('<a>')
-          .addClass("action-button show-images")
+          .addClass("action-button pull-right show-images")
           .css({'font-weight':'normal'})
-          .text(' Show Images')
+          .text(' ' + __('Show Images'))
           .click(function(ev) {
             imgs.each(function(i, img) {
               $.showNonLocalImage(img);
diff --git a/scp/js/tips.js b/scp/js/tips.js
index 9bdd5144db1b88c2faa8711fb065d38a7b955c07..93f4f14c75d49d1c151e27ffc787e1abb42f3078 100644
--- a/scp/js/tips.js
+++ b/scp/js/tips.js
@@ -10,8 +10,16 @@ jQuery(function() {
             var tip_shadow = $('<div>').addClass('tip_shadow');
             var tip_content = $('<div>').addClass('tip_content').load(url, function() {
                 tip_content.prepend('<a href="#" class="tip_close"><i class="icon-remove-circle"></i></a>').append(tip_arrow);
-                if ($(window).width() < tip_content.outerWidth() + the_tip.position().left) {
-                    console.log(x_pos, tip_content.outerWidth(), elem.width());
+            var width = $(window).width(),
+                rtl = $('html').hasClass('rtl'),
+                size = tip_content.outerWidth(),
+                left = the_tip.position().left,
+                left_room = left - size,
+                right_room = width - size - left,
+                flip = rtl
+                    ? (left_room > 0 && left_room > right_room)
+                    : (right_room < 0 && left_room > right_room);
+                if (flip) {
                     the_tip.css({'left':x_pos-tip_content.outerWidth()-elem.width()-32+'px'});
                     tip_box.addClass('right');
                     tip_arrow.addClass('flip-x');
@@ -36,7 +44,7 @@ jQuery(function() {
                 || $('#content').data('tipNamespace')
                 || $('meta[name=tip-namespace]').attr('content');
             if (!namespace)
-                return false;
+                return $.Deferred().resolve().promise();
             else if (!cache[namespace])
                 cache[namespace] = {
                   dfd: dfd = $.Deferred(),
@@ -105,7 +113,16 @@ jQuery(function() {
             tip_timer = setTimeout(function() {
                 $('.tip_box').remove();
                 $('body').append(the_tip.hide().fadeIn());
-                if ($(window).width() < tip_content.outerWidth() + the_tip.position().left) {
+                var width = $(window).width(),
+                    rtl = $('html').hasClass('rtl'),
+                    size = tip_content.outerWidth(),
+                    left = the_tip.position().left,
+                    left_room = left - size,
+                    right_room = width - size - left,
+                    flip = rtl
+                        ? (left_room > 0 && left_room > right_room)
+                        : (right_room < 0 && left_room > right_room);
+                if (flip) {
                     the_tip.css({'left':x_pos-tip_content.outerWidth()-40+'px'});
                     tip_box.addClass('right');
                     tip_arrow.addClass('flip-x');
@@ -129,6 +146,8 @@ jQuery(function() {
                 clearTimeout(tip_timer);
                 return;
             }
+            if (!section)
+                return;
             tip_content.append(
                 $('<h1>')
                     .append('<i class="icon-info-sign faded"> ')
diff --git a/scp/js/upgrader.js b/scp/js/upgrader.js
index 2ac77d99f86c5db1b2a54d36f4afc709e2b9bad5..a10c02626b92fcb6a87680bcabe9c699a627d9a6 100644
--- a/scp/js/upgrader.js
+++ b/scp/js/upgrader.js
@@ -38,7 +38,7 @@ jQuery(function($) {
                 success: function(res) {
                     $('#main #task').html(res);
                     $('#upgrading #action').html(res);
-                    $('#upgrading #msg').html('Still busy... smile #'+count);
+                    $('#upgrading #msg').html(__('Still busy... smile #')+count);
                 },
                 statusCode: {
                     200: function() {
@@ -46,19 +46,19 @@ jQuery(function($) {
                     },
 
                     201: function() {
-                        $('#upgrading #msg').html("Cleaning up!...");
+                        $('#upgrading #msg').html(__("Cleaning up!..."));
                         setTimeout(function() { location.href =url+'?c='+count+'&r='+Math.floor((Math.random()*100)+1); }, 3000);
                     }
                 },
                 error: function(jqXHR, textStatus, errorThrown) {
-                    $('#upgrading #action').html('Error occurred. Aborting...');
+                    $('#upgrading #action').html(__('Error occurred.  Aborting...'));
                     switch(jqXHR.status) {
                         case 404:
-                            $('#upgrading #msg').html("Manual upgrade required (ajax failed)");
+                            $('#upgrading #msg').html(__("Manual upgrade required (ajax failed)"));
                             setTimeout(function() { location.href =url+'?m=manual&c='+count+'&r='+Math.floor((Math.random()*100)+1); }, 2000);
                             break;
                         default:
-                            $('#upgrading #msg').html("Something went wrong");
+                            $('#upgrading #msg').html(__("Something went wrong"));
                             setTimeout(function() { location.href =url+'?c='+count+'&r='+Math.floor((Math.random()*100)+1); }, 2000);
                     }
                 }
diff --git a/scp/kb.php b/scp/kb.php
index 0194beb2615b09b530ab07ce41f612c1fe866419..67a09fd68f0cd7ec115ca75d9c050a05efd7a123 100644
--- a/scp/kb.php
+++ b/scp/kb.php
@@ -15,9 +15,10 @@
 **********************************************************************/
 require('staff.inc.php');
 require_once(INCLUDE_DIR.'class.faq.php');
+
 $category=null;
 if($_REQUEST['cid'] && !($category=Category::lookup($_REQUEST['cid'])))
-    $errors['err']='Unknown or invalid FAQ category';
+    $errors['err']=__('Unknown or invalid FAQ category');
 
 $inc='faq-categories.inc.php'; //KB landing page.
 if($category && $_REQUEST['a']!='search') {
diff --git a/scp/l.php b/scp/l.php
index f05349be04db6204b5f2de494908a31ea19cfa02..ec4705a0f2763101c2ac2b1b96c3af174793316a 100644
--- a/scp/l.php
+++ b/scp/l.php
@@ -18,11 +18,11 @@ require_once 'staff.inc.php';
 
 # PHP < 5.4.7 will not handle a URL like //host.tld/path correctly
 if (!($url=trim($_GET['url'])))
-    Http::response(422, 'Invalid URL');
+    Http::response(422, __('Invalid URL'));
 
 $check = (strpos($url, '//') === 0) ? 'http:' . $url : $url;
 if (!Validator::is_url($check) || !$ost->validateLinkToken($_GET['auth']))
-    Http::response(403, 'URL link not authorized');
+    Http::response(403, __('URL link not authorized'));
 elseif (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === false)
     Http::redirect($url);
 ?>
diff --git a/scp/lists.php b/scp/lists.php
index 57ffad7a26374d13768da9e826a25f6252a5ec7a..5247a0643c36e1aed9c9b8b73c6ff75661aa0dff 100644
--- a/scp/lists.php
+++ b/scp/lists.php
@@ -1,120 +1,137 @@
 <?php
 require('admin.inc.php');
-require_once(INCLUDE_DIR."/class.dynamic_forms.php");
+require_once(INCLUDE_DIR.'class.list.php');
+
 
 $list=null;
-if($_REQUEST['id'] && !($list=DynamicList::lookup($_REQUEST['id'])))
-    $errors['err']='Unknown or invalid dynamic list ID.';
+$criteria=array();
+if ($_REQUEST['id'])
+    $criteria['id'] = $_REQUEST['id'];
+elseif ($_REQUEST['type'])
+    $criteria['type'] = $_REQUEST['type'];
+
+if ($criteria) {
+    $list = DynamicList::lookup($criteria);
+
+    if ($list)
+         $form = $list->getForm();
+    else
+        $errors['err']=sprintf(__('%s: Unknown or invalid ID.'),
+            __('custom list'));
+}
 
-if ($list)
-    $form = $list->getForm();
+$errors = array();
+$max_isort = 0;
 
 if($_POST) {
-    $fields = array('name', 'name_plural', 'sort_mode', 'notes');
-    $required = array('name');
     switch(strtolower($_POST['do'])) {
         case 'update':
-            foreach ($fields as $f)
-                if (in_array($f, $required) && !$_POST[$f])
-                    $errors[$f] = sprintf('%s is required',
-                        mb_convert_case($f, MB_CASE_TITLE));
-                elseif (isset($_POST[$f]))
-                    $list->set($f, $_POST[$f]);
-            if ($errors)
-                $errors['err'] = 'Unable to update custom list. Correct any error(s) below and try again.';
-            elseif ($list->save(true))
-                $msg = 'Custom list updated successfully';
-            else
-                $errors['err'] = 'Unable to update custom list. Unknown internal error';
-
-            foreach ($list->getAllItems() as $item) {
-                $id = $item->get('id');
-                if ($_POST["delete-$id"] == 'on') {
-                    $item->delete();
-                    continue;
-                }
-                foreach (array('sort','value','extra') as $i)
-                    if (isset($_POST["$i-$id"]))
-                        $item->set($i, $_POST["$i-$id"]);
+            if (!$list)
+                $errors['err']=sprintf(__('%s: Unknown or invalid ID.'),
+                    __('custom list'));
+            elseif ($list->update($_POST, $errors)) {
+                // Update items
+                $items = array();
+                foreach ($list->getAllItems() as $item) {
+                    $id = $item->getId();
+                    if ($_POST["delete-item-$id"] == 'on' && $item->isDeletable()) {
+                        $item->delete();
+                        continue;
+                    }
 
-                if ($_POST["disable-$id"] == 'on')
-                    $item->disable();
-                else
-                    $item->enable();
+                    $ht = array(
+                            'value' => $_POST["value-$id"],
+                            'abbrev' => $_POST["abbrev-$id"],
+                            'sort' => $_POST["sort-$id"],
+                            );
+                    $value = mb_strtolower($ht['value']);
+                    if (!$value)
+                        $errors["value-$id"] = __('Value required');
+                    elseif (in_array($value, $items))
+                        $errors["value-$id"] = __('Value already in-use');
+                    elseif ($item->update($ht, $errors)) {
+                        if ($_POST["disable-$id"] == 'on')
+                            $item->disable();
+                        elseif(!$item->isEnabled() && $item->isEnableable())
+                            $item->enable();
 
-                $item->save();
-            }
+                        $item->save();
+                        $items[] = $value;
+                    }
 
-            $names = array();
-            if (!$form) {
-                $form = DynamicForm::create(array(
-                    'type'=>'L'.$_REQUEST['id'],
-                    'title'=>$_POST['name'] . ' Properties'
-                ));
-                $form->save(true);
-            }
-            foreach ($form->getDynamicFields() as $field) {
-                $id = $field->get('id');
-                if ($_POST["delete-$id"] == 'on' && $field->isDeletable()) {
-                    $field->delete();
-                    // Don't bother updating the field
-                    continue;
+                    $max_isort = max($max_isort, $_POST["sort-$id"]);
                 }
-                if (isset($_POST["type-$id"]) && $field->isChangeable())
-                    $field->set('type', $_POST["type-$id"]);
-                if (isset($_POST["name-$id"]) && !$field->isNameForced())
-                    $field->set('name', $_POST["name-$id"]);
-                # TODO: make sure all help topics still have all required fields
-                foreach (array('sort','label') as $f) {
-                    if (isset($_POST["prop-$f-$id"])) {
-                        $field->set($f, $_POST["prop-$f-$id"]);
+
+                // Update properties
+                if (!$errors && ($form = $list->getForm())) {
+                    $names = array();
+                    foreach ($form->getDynamicFields() as $field) {
+                        $id = $field->get('id');
+                        if ($_POST["delete-prop-$id"] == 'on' && $field->isDeletable()) {
+                            $field->delete();
+                            // Don't bother updating the field
+                            continue;
+                        }
+                        if (isset($_POST["type-$id"]) && $field->isChangeable())
+                            $field->set('type', $_POST["type-$id"]);
+                        if (isset($_POST["name-$id"]) && !$field->isNameForced())
+                            $field->set('name', $_POST["name-$id"]);
+
+                        foreach (array('sort','label') as $f) {
+                            if (isset($_POST["prop-$f-$id"])) {
+                                $field->set($f, $_POST["prop-$f-$id"]);
+                            }
+                        }
+                        if (in_array($field->get('name'), $names))
+                            $field->addError(__('Field variable name is not unique'), 'name');
+                        if (preg_match('/[.{}\'"`; ]/u', $field->get('name')))
+                            $field->addError(__('Invalid character in variable name. Please use letters and numbers only.'), 'name');
+                        if ($field->get('name'))
+                            $names[] = $field->get('name');
+                        if ($field->isValid())
+                            $field->save();
+                        else
+                            # notrans (not shown)
+                            $errors["field-$id"] = 'Field has validation errors';
+                        // Keep track of the last sort number
+                        $max_sort = max($max_sort, $field->get('sort'));
                     }
                 }
-                if (in_array($field->get('name'), $names))
-                    $field->addError('Field variable name is not unique', 'name');
-                if (preg_match('/[.{}\'"`; ]/u', $field->get('name')))
-                    $field->addError('Invalid character in variable name. Please use letters and numbers only.', 'name');
-                if ($field->get('name'))
-                    $names[] = $field->get('name');
-                if ($field->isValid())
-                    $field->save();
-                else
-                    # notrans (not shown)
-                    $errors["field-$id"] = 'Field has validation errors';
-                // Keep track of the last sort number
-                $max_sort = max($max_sort, $field->get('sort'));
-            }
-            break;
-        case 'add':
-            foreach ($fields as $f)
-                if (in_array($f, $required) && !$_POST[$f])
-                    $errors[$f] = sprintf('%s is required',
-                        mb_convert_case($f, MB_CASE_TITLE));
-            $list = DynamicList::create(array(
-                'name'=>$_POST['name'],
-                'name_plural'=>$_POST['name_plural'],
-                'sort_mode'=>$_POST['sort_mode'],
-                'notes'=>$_POST['notes']));
-
-            $form = DynamicForm::create(array(
-                'title'=>$_POST['name'] . ' Properties'
-            ));
 
-            if ($errors)
-                $errors['err'] = 'Unable to create custom list. Correct any error(s) below and try again.';
-            elseif (!$list->save(true))
-                $errors['err'] = 'Unable to create custom list: Unknown internal error';
+                if ($errors)
+                     $errors['err'] = $errors['err'] ?: sprintf(__('Unable to update %s. Correct error(s) below and try again!'),
+                        __('custom list items'));
+                else {
+                    $list->_items = null;
+                    $msg = sprintf(__('Successfully updated %s'),
+                        __('this custom list'));
+                }
 
-            $form->set('type', 'L'.$list->get('id'));
-            if (!$errors && !$form->save(true))
-                $errors['err'] = 'Unable to create properties for custom list: Unknown internal error';
+            } elseif ($errors)
+                $errors['err'] = $errors['err'] ?: sprintf(__('Unable to update %s. Correct error(s) below and try again!'),
+                    __('this custom list'));
             else
-                $msg = 'Custom list added successfully';
+                $errors['err']=sprintf(__('Unable to update %s.'), __('this custom list'))
+                    .' '.__('Internal error occurred');
+
+            break;
+        case 'add':
+            if ($list=DynamicList::add($_POST, $errors)) {
+                 $msg = sprintf(__('Successfully added %s'),
+                    __('this custom list'));
+            } elseif ($errors) {
+                $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                    __('this custom list'));
+            } else {
+                $errors['err']=sprintf(__('Unable to add %s.'), __('this custom list'))
+                    .' '.__('Internal error occurred');
+            }
             break;
 
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err'] = 'You must select at least one API key';
+                $errors['err'] = sprintf(__('You must select at least %s'),
+                    __('one custom list'));
             } else {
                 $count = count($_POST['ids']);
                 switch(strtolower($_POST['a'])) {
@@ -125,32 +142,31 @@ if($_POST) {
                                 $i++;
                         }
                         if ($i && $i==$count)
-                            $msg = 'Selected custom lists deleted successfully';
+                            $msg = sprintf(__('Successfully deleted %s'),
+                                _N('selected custom list', 'selected custom lists', $count));
                         elseif ($i > 0)
-                            $warn = "$i of $count selected lists deleted";
+                            $warn = sprintf(__('%1$d of %2$d %3$s deleted'), $i, $count,
+                                _N('selected custom list', 'selected custom lists', $count));
                         elseif (!$errors['err'])
-                            $errors['err'] = 'Unable to delete selected custom lists'
-                                .' &mdash; they may be in use on a custom form';
+                            $errors['err'] = sprintf(__('Unable to delete %s — they may be in use on a custom form'),
+                                _N('selected custom list', 'selected custom lists', $count));
                         break;
                 }
             }
             break;
     }
 
-    if ($list) {
-        for ($i=0; isset($_POST["prop-sort-new-$i"]); $i++) {
+    if ($list && $list->allowAdd()) {
+        for ($i=0; isset($_POST["sort-new-$i"]); $i++) {
             if (!$_POST["value-new-$i"])
                 continue;
-            $item = DynamicListItem::create(array(
-                'list_id'=>$list->get('id'),
-                'sort'=>$_POST["sort-new-$i"],
-                'value'=>$_POST["value-new-$i"],
-                'extra'=>$_POST["extra-new-$i"]
-            ));
-            $item->save();
+
+            $list->addItem(array(
+                        'value' => $_POST["value-new-$i"],
+                        'abbrev' =>$_POST["abbrev-new-$i"],
+                        'sort' => $_POST["sort-new-$i"] ?: ++$max_isort,
+                        ), $errors);
         }
-        # Invalidate items cache
-        $list->_items = false;
     }
 
     if ($form) {
@@ -158,12 +174,11 @@ if($_POST) {
             if (!$_POST["prop-label-new-$i"])
                 continue;
             $field = DynamicFormField::create(array(
-                'form_id'=>$form->get('id'),
-                'sort'=>$_POST["prop-sort-new-$i"]
-                    ? $_POST["prop-sort-new-$i"] : ++$max_sort,
-                'label'=>$_POST["prop-label-new-$i"],
-                'type'=>$_POST["type-new-$i"],
-                'name'=>$_POST["name-new-$i"],
+                'form_id' => $form->get('id'),
+                'sort' => $_POST["prop-sort-new-$i"] ?: ++$max_sort,
+                'label' => $_POST["prop-label-new-$i"],
+                'type' => $_POST["type-new-$i"],
+                'name' => $_POST["name-new-$i"],
             ));
             $field->setForm($form);
             if ($field->isValid())
diff --git a/scp/login.php b/scp/login.php
index b110ac6359d8c39f5feba665dc83e89c0238dabb..ad07a831b9d329c5044339fb08b5b726b178baa9 100644
--- a/scp/login.php
+++ b/scp/login.php
@@ -16,6 +16,10 @@
 require_once('../main.inc.php');
 if(!defined('INCLUDE_DIR')) die('Fatal Error. Kwaheri!');
 
+// Bootstrap gettext translations. Since no one is yet logged in, use the
+// system or browser default
+TextDomain::configureForUser();
+
 require_once(INCLUDE_DIR.'class.staff.php');
 require_once(INCLUDE_DIR.'class.csrf.php');
 
@@ -23,7 +27,7 @@ $content = Page::lookup(Page::getIdByType('banner-staff'));
 
 $dest = $_SESSION['_staff']['auth']['dest'];
 $msg = $_SESSION['_staff']['auth']['msg'];
-$msg = $msg ?: ($content ? $content->getName() : 'Authentication Required');
+$msg = $msg ?: ($content ? $content->getName() : __('Authentication Required'));
 $dest=($dest && (!strstr($dest,'login.php') && !strstr($dest,'ajax.php')))?$dest:'index.php';
 $show_reset = false;
 if($_POST) {
@@ -37,7 +41,7 @@ if($_POST) {
         exit;
     }
 
-    $msg = $errors['err']?$errors['err']:'Invalid login';
+    $msg = $errors['err']?$errors['err']:__('Invalid login');
     $show_reset = true;
 }
 elseif ($_GET['do']) {
diff --git a/scp/logs.php b/scp/logs.php
index 2931cd9c1f6453dc9002c1db69e92f64e8eef210..df17796687040d053e5ce75811a99d9de0299b0c 100644
--- a/scp/logs.php
+++ b/scp/logs.php
@@ -19,7 +19,8 @@ if($_POST){
     switch(strtolower($_POST['do'])){
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err'] = 'You must select at least one log to delete';
+                $errors['err'] = sprintf(__('You must select at least %s'),
+                    __('one log entry'));
             } else {
                 $count=count($_POST['ids']);
                 if($_POST['a'] && !strcasecmp($_POST['a'], 'delete')) {
@@ -28,18 +29,21 @@ if($_POST){
                         .' WHERE log_id IN ('.implode(',', db_input($_POST['ids'])).')';
                     if(db_query($sql) && ($num=db_affected_rows())){
                         if($num==$count)
-                            $msg='Selected logs deleted successfully';
+                            $msg=sprintf(__('Successfully deleted %s'),
+                                _N('selected log entry', 'selected log entries', $count));
                         else
-                            $warn="$num of $count selected logs deleted";
+                            $warn=sprintf(__('%1$d of %2$d %3$s deleted'), $num, $count,
+                                _N('selected log entry', 'selected log entries', $count));
                     } elseif(!$errors['err'])
-                        $errors['err']='Unable to delete selected logs';
+                        $errors['err']=sprintf(__('Unable to delete %s'),
+                            _N('selected log entry', 'selected log entries', $count));
                 } else {
-                    $errors['err']='Unknown action - get technical help';
+                    $errors['err']=__('Unknown action - get technical help.');
                 }
             }
             break;
         default:
-            $errors['err']='Unknown command/action';
+            $errors['err']=__('Unknown action');
             break;
     }
 }
diff --git a/scp/orgs.php b/scp/orgs.php
index 373864afd09b30f321af48d20890ea197d32538b..22cd2aeca213b7b6452b577d49390588fe87d822 100644
--- a/scp/orgs.php
+++ b/scp/orgs.php
@@ -23,22 +23,23 @@ if ($_POST) {
     switch ($_REQUEST['a']) {
     case 'import-users':
         if (!$org) {
-            $errors['err'] = 'Organization ID must be specified for import';
+            $errors['err'] = __('Organization ID must be specified for import');
             break;
         }
         $status = User::importFromPost($_FILES['import'] ?: $_POST['pasted'],
             array('org_id'=>$org->getId()));
         if (is_numeric($status))
-            $msg = "Successfully imported $status clients";
+            $msg = sprintf(__('Successfully imported %1$d %2$s'), $status,
+                _N('end user', 'end users', $status));
         else
             $errors['err'] = $status;
         break;
     case 'remove-users':
         if (!$org)
-            $errors['err'] = ' Trying to remove users from unknown
-                 organization';
+            $errors['err'] = __('Trying to remove end users from an unknown organization');
         elseif (!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-            $errors['err'] = 'You must select at least one user to remove';
+            $errors['err'] = sprintf(__('You must select at least %s'),
+                __('one end user'));
         } else {
             $i = 0;
             foreach ($_POST['ids'] as $k=>$v) {
@@ -47,25 +48,28 @@ if ($_POST) {
             }
             $num = count($_POST['ids']);
             if ($i && $i == $num)
-                $msg = 'Selected users removed successfully';
+                $msg = sprintf(__('Successfully removed %s'),
+                    _N('selected end user', 'selected end users', $count));
             elseif ($i > 0)
-                $warn = "$i of $num selected users removed";
+                $warn = sprintf(__('%1$d of %2$d %3$s removed'), $i, $count,
+                    _N('selected end user', 'selected end users', $count));
             elseif (!$errors['err'])
-                $errors['err'] = 'Unable to remove selected users';
+                $errors['err'] = sprintf(__('Unable to remove %s'),
+                    _N('selected end user', 'selected end users', $count));
         }
         break;
     default:
-        $errors['err'] = 'Unknown action';
+        $errors['err'] = __('Unknown action');
     }
 } elseif ($_REQUEST['a'] == 'export') {
     require_once(INCLUDE_DIR.'class.export.php');
     $ts = strftime('%Y%m%d');
     if (!($token=$_REQUEST['qh']))
-        $errors['err'] = 'Query token required';
+        $errors['err'] = __('Query token required');
     elseif (!($query=$_SESSION['orgs_qs_'.$token]))
-        $errors['err'] = 'Query token not found';
-    elseif (!Export::saveOrganizations($query, "organizations-$ts.csv", 'csv'))
-        $errors['err'] = 'Internal error: Unable to export results';
+        $errors['err'] = __('Query token not found');
+    elseif (!Export::saveOrganizations($query, __('organizations')."-$ts.csv", 'csv'))
+        $errors['err'] = __('Internal error: Unable to export results');
 }
 
 $page = $org? 'org-view.inc.php' : 'orgs.inc.php';
diff --git a/scp/pages.php b/scp/pages.php
index c809c50b6d9f110ba36414909fa5fbdda0520e25..c761d92e2e84a1fdcb57b5d02e134213711c81d5 100644
--- a/scp/pages.php
+++ b/scp/pages.php
@@ -18,27 +18,30 @@ require_once(INCLUDE_DIR.'class.page.php');
 
 $page = null;
 if($_REQUEST['id'] && !($page=Page::lookup($_REQUEST['id'])))
-   $errors['err']='Unknown or invalid page';
+   $errors['err']=sprintf(__('%s: Unknown or invalid'), __('site page'));
 
 if($_POST) {
     switch(strtolower($_POST['do'])) {
         case 'add':
             if(($pageId=Page::create($_POST, $errors))) {
                 $_REQUEST['a'] = null;
-                $msg='Page added successfully';
+                $msg=sprintf(__('Successfully added %s'), Format::htmlchars($_POST['name']));
                 // Attach inline attachments from the editor
                 if ($page = Page::lookup($pageId))
                     $page->attachments->upload(
                         Draft::getAttachmentIds($_POST['body']), true);
                 Draft::deleteForNamespace('page');
             } elseif(!$errors['err'])
-                $errors['err'] = 'Unable to add page. Try again!';
+                $errors['err'] = sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                    __('this site page'));
         break;
         case 'update':
             if(!$page)
-                $errors['err'] = 'Invalid or unknown page';
+                $errors['err'] = sprintf(__('%s: Invalid or unknown'),
+                    __('site page'));
             elseif($page->update($_POST, $errors)) {
-                $msg='Page updated successfully';
+                $msg=sprintf(__('Successfully updated %s'),
+                    __('this site page'));
                 $_REQUEST['a']=null; //Go back to view
                 // Attach inline attachments from the editor
                 $page->attachments->deleteInlines();
@@ -47,13 +50,16 @@ if($_POST) {
                     true);
                 Draft::deleteForNamespace('page.'.$page->getId());
             } elseif(!$errors['err'])
-                $errors['err'] = 'Unable to update page. Try again!';
+                $errors['err'] = sprintf(__('Unable to update %s. Correct error(s) below and try again.'),
+                    __('this site page'));
             break;
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err'] = 'You must select at least one page.';
+                $errors['err'] = sprintf(__('You must select at least %s.'),
+                    __('one site page'));
             } elseif(array_intersect($_POST['ids'], $cfg->getDefaultPages()) && strcasecmp($_POST['a'], 'enable')) {
-                 $errors['err'] = 'One or more of the selected pages is in-use and CANNOT be disabled/deleted.';
+                $errors['err'] = sprintf(__('One or more of the %s is in-use and CANNOT be disabled/deleted.'),
+                    _N('selected site page', 'selected site pages', 2));
             } else {
                 $count=count($_POST['ids']);
                 switch(strtolower($_POST['a'])) {
@@ -62,11 +68,14 @@ if($_POST) {
                             .' WHERE id IN ('.implode(',', db_input($_POST['ids'])).')';
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected pages enabled';
+                                $msg = sprintf(__('Successfully enabled %s'),
+                                    _N('selected site page', 'selected site pages', $count));
                             else
-                                $warn = "$num of $count selected pages enabled";
+                                $warn = sprintf(__('%1$d of %2$d %3$s enabled'), $num, $count,
+                                    _N('selected site page', 'selected site pages', $count));
                         } else {
-                            $errors['err'] = 'Unable to enable selected pages';
+                            $errors['err'] = sprintf(__('Unable to enable %s'),
+                                _N('selected site page', 'selected site pages', $count));
                         }
                         break;
                     case 'disable':
@@ -77,11 +86,14 @@ if($_POST) {
                         }
 
                         if($i && $i==$count)
-                            $msg = 'Selected pages disabled';
+                            $msg = sprintf(__('Successfully disabled %s'),
+                                _N('selected site page', 'selected site pages', $count));
                         elseif($i>0)
-                            $warn = "$num of $count selected pages disabled";
+                            $warn = sprintf(__('%1$d of %2$d %3$s disabled'), $i, $count,
+                                _N('selected site page', 'selected site pages', $count));
                         elseif(!$errors['err'])
-                            $errors['err'] = 'Unable to disable selected pages';
+                            $errors['err'] = sprintf(__('Unable to disable %s'),
+                                _N('selected site page', 'selected site pages', $count));
                         break;
                     case 'delete':
                         $i=0;
@@ -91,19 +103,22 @@ if($_POST) {
                         }
 
                         if($i && $i==$count)
-                            $msg = 'Selected pages deleted successfully';
+                            $msg = sprintf(__('Successfully deleted %s'),
+                                _N('selected site page', 'selected site pages', $count));
                         elseif($i>0)
-                            $warn = "$i of $count selected pages deleted";
+                            $warn = sprintf(__('%1$d of %2$d %3$s deleted'), $i, $count,
+                                _N('selected site page', 'selected site pages', $count));
                         elseif(!$errors['err'])
-                            $errors['err'] = 'Unable to delete selected pages';
+                            $errors['err'] = sprintf(__('Unable to delete %s'),
+                                _N('selected site page', 'selected site pages', $count));
                         break;
                     default:
-                        $errors['err']='Unknown action - get technical help.';
+                        $errors['err']=__('Unknown action - get technical help.');
                 }
             }
             break;
         default:
-            $errors['err']='Unknown action/command';
+            $errors['err']=__('Unknown action');
             break;
     }
 }
diff --git a/scp/plugins.php b/scp/plugins.php
index 44dda73b80897b668caafe38638c163321a31e64..ccc856e3a3f744b3060affde8a7dba95fd54650b 100644
--- a/scp/plugins.php
+++ b/scp/plugins.php
@@ -3,7 +3,8 @@ require('admin.inc.php');
 require_once(INCLUDE_DIR."/class.plugin.php");
 
 if($_REQUEST['id'] && !($plugin=Plugin::lookup($_REQUEST['id'])))
-    $errors['err']='Unknown or invalid plugin ID.';
+    $errors['err']=sprintf(__('%s: Unknown or invalid ID.'),
+        __('plugin'));
 
 if($_POST) {
     switch(strtolower($_POST['do'])) {
@@ -14,7 +15,8 @@ if($_POST) {
         break;
     case 'mass_process':
         if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-            $errors['err'] = 'You must select at least one plugin';
+            $errors['err'] = sprintf(__('You must select at least %s'),
+                __('one plugin'));
         } else {
             $count = count($_POST['ids']);
             switch(strtolower($_POST['a'])) {
@@ -44,7 +46,8 @@ if($_POST) {
         break;
     case 'install':
         if ($ost->plugins->install($_POST['install_path']))
-            $msg = 'Plugin successfully installed';
+            $msg = sprintf(__('Successfully installed %s'),
+                __('a plugin'));
         break;
     }
 }
diff --git a/scp/profile.php b/scp/profile.php
index 27ed0b41425184ebc363e9c1019c63cedc7238c1..d25d545d45f374211756f638ba7ff8203b19bb56 100644
--- a/scp/profile.php
+++ b/scp/profile.php
@@ -15,29 +15,31 @@
 **********************************************************************/
 
 require_once('staff.inc.php');
+require_once(INCLUDE_DIR.'class.export.php');       // For paper sizes
+
 $msg='';
 $staff=Staff::lookup($thisstaff->getId());
 if($_POST && $_POST['id']!=$thisstaff->getId()) { //Check dummy ID used on the form.
- $errors['err']='Internal Error. Action Denied';
+ $errors['err']=__('Internal Error. Action Denied');
 } elseif(!$errors && $_POST) { //Handle post
 
     if(!$staff)
-        $errors['err']='Unknown or invalid staff';
+        $errors['err']=sprintf(__('%s: Unknown or invalid'), __('agent'));
     elseif($staff->updateProfile($_POST,$errors)){
-        $msg='Profile updated successfully';
+        $msg=__('Profile updated successfully');
         $thisstaff->reload();
         $staff->reload();
         $_SESSION['TZ_OFFSET']=$thisstaff->getTZoffset();
         $_SESSION['TZ_DST']=$thisstaff->observeDaylight();
     }elseif(!$errors['err'])
-        $errors['err']='Profile update error. Try correcting the errors below and try again!';
+        $errors['err']=__('Profile update error. Try correcting the errors below and try again!');
 }
 
 //Forced password Change.
 if($thisstaff->forcePasswdChange() && !$errors['err'])
-    $errors['err']=sprintf('<b>Hi %s</b> - You must change your password to continue!',$thisstaff->getFirstName());
+    $errors['err']=sprintf(__('<b>Hi %s</b> - You must change your password to continue!'),$thisstaff->getFirstName());
 elseif($thisstaff->onVacation() && !$warn)
-    $warn=sprintf('<b>Welcome back %s</b>! You are listed as \'on vacation\' Please let your manager know that you are back.',$thisstaff->getFirstName());
+    $warn=sprintf(__("<b>Welcome back %s</b>! You are listed as 'on vacation' Please let your manager know that you are back."),$thisstaff->getFirstName());
 
 $inc='profile.inc.php';
 $nav->setTabActive('dashboard');
diff --git a/scp/pwreset.php b/scp/pwreset.php
index f5eed25fc0ed98e37cfa4d18dfc098c858bffe77..afca9196e44afad867749f0914ae2c86b6969d35 100644
--- a/scp/pwreset.php
+++ b/scp/pwreset.php
@@ -24,28 +24,32 @@
 require_once('../main.inc.php');
 if(!defined('INCLUDE_DIR')) die('Fatal Error. Kwaheri!');
 
+// Bootstrap gettext translations. Since no one is yet logged in, use the
+// system or browser default
+TextDomain::configureForUser();
+
 require_once(INCLUDE_DIR.'class.staff.php');
 require_once(INCLUDE_DIR.'class.csrf.php');
 
 $tpl = 'pwreset.php';
 if($_POST) {
     if (!$ost->checkCSRFToken()) {
-        Http::response(400, 'Valid CSRF Token Required');
+        Http::response(400, __('Valid CSRF Token Required'));
         exit;
     }
     switch ($_POST['do']) {
         case 'sendmail':
             if (($staff=Staff::lookup($_POST['userid']))) {
                 if (!$staff->hasPassword()) {
-                    $msg = 'Unable to reset password. Contact your administrator';
+                    $msg = __('Unable to reset password. Contact your administrator');
                 }
                 elseif (!$staff->sendResetEmail()) {
                     $tpl = 'pwreset.sent.php';
                 }
             }
             else
-                $msg = 'Unable to verify username '
-                    .Format::htmlchars($_POST['userid']);
+                $msg = sprintf(__('Unable to verify username %s'),
+                    Format::htmlchars($_POST['userid']));
             break;
         case 'newpasswd':
             // TODO: Compare passwords
@@ -62,7 +66,7 @@ if($_POST) {
     }
 }
 elseif ($_GET['token']) {
-    $msg = 'Please enter your username or email';
+    $msg = __('Please enter your username or email');
     $_config = new Config('pwreset');
     if (($id = $_config->get($_GET['token']))
             && ($staff = Staff::lookup($id)))
@@ -72,10 +76,10 @@ elseif ($_GET['token']) {
         header('Location: index.php');
 }
 elseif ($cfg->allowPasswordReset()) {
-    $msg = 'Enter your username or email address below';
+    $msg = __('Enter your username or email address below');
 }
 else {
-    $_SESSION['_staff']['auth']['msg']='Password resets are disabled';
+    $_SESSION['_staff']['auth']['msg']=__('Password resets are disabled');
     return header('Location: index.php');
 }
 define("OSTSCPINC",TRUE); //Make includes happy!
diff --git a/scp/settings.php b/scp/settings.php
index 96ee4b97c20c50fde39cce47bc9f28edeb83a1c9..b824ef7cabbf0305fae1c56d4c199147ec59b473 100644
--- a/scp/settings.php
+++ b/scp/settings.php
@@ -13,25 +13,26 @@
 
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
+
 require('admin.inc.php');
 $errors=array();
 $settingOptions=array(
     'system' =>
-        array('System Settings', 'settings.system'),
+        array(__('System Settings'), 'settings.system'),
     'tickets' =>
-        array('Ticket Settings and Options', 'settings.ticket'),
+        array(__('Ticket Settings and Options'), 'settings.ticket'),
     'emails' =>
-        array('Email Settings', 'settings.email'),
+        array(__('Email Settings'), 'settings.email'),
     'pages' =>
-        array('Site Pages', 'settings.pages'),
+        array(__('Site Pages'), 'settings.pages'),
     'access' =>
-        array('Access Control', 'settings.access'),
+        array(__('Access Control'), 'settings.access'),
     'kb' =>
-        array('Knowledgebase Settings', 'settings.kb'),
+        array(__('Knowledgebase Settings'), 'settings.kb'),
     'autoresp' =>
-        array('Autoresponder Settings', 'settings.autoresponder'),
+        array(__('Autoresponder Settings'), 'settings.autoresponder'),
     'alerts' =>
-        array('Alerts and Notices Settings', 'settings.alerts'),
+        array(__('Alerts and Notices Settings'), 'settings.alerts'),
 );
 //Handle a POST.
 $target=($_REQUEST['t'] && $settingOptions[$_REQUEST['t']])?$_REQUEST['t']:'system';
@@ -41,9 +42,9 @@ if (isset($settingOptions[$target]))
 
 if($page && $_POST && !$errors) {
     if($cfg && $cfg->updateSettings($_POST,$errors)) {
-        $msg=Format::htmlchars($page[0]).' Updated Successfully';
+        $msg=sprintf(__('Successfully updated %s'), Format::htmlchars($page[0]));
     } elseif(!$errors['err']) {
-        $errors['err']='Unable to update settings - correct errors below and try again';
+        $errors['err']=__('Unable to update settings - correct errors below and try again');
     }
 }
 
diff --git a/scp/slas.php b/scp/slas.php
index e4434da4668a3eabc440f069eaa51df6980c8d28..b47c73092e21344b8b81dceba0f3f2036a1ad697 100644
--- a/scp/slas.php
+++ b/scp/slas.php
@@ -18,30 +18,37 @@ include_once(INCLUDE_DIR.'class.sla.php');
 
 $sla=null;
 if($_REQUEST['id'] && !($sla=SLA::lookup($_REQUEST['id'])))
-    $errors['err']='Unknown or invalid API key ID.';
+    $errors['err']=sprintf(__('%s: Unknown or invalid ID.'),
+        __('SLA plan'));
 
 if($_POST){
     switch(strtolower($_POST['do'])){
         case 'update':
             if(!$sla){
-                $errors['err']='Unknown or invalid SLA plan.';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'),
+                    __('SLA plan'));
             }elseif($sla->update($_POST,$errors)){
-                $msg='SLA plan updated successfully';
+                $msg=sprintf(__('Successfully updated %s'),
+                    __('this SLA plan'));
             }elseif(!$errors['err']){
-                $errors['err']='Error updating SLA plan. Try again!';
+                $errors['err']=sprintf(__('Error updating %s. Try again!'),
+                    __('this SLA plan'));
             }
             break;
         case 'add':
             if(($id=SLA::create($_POST,$errors))){
-                $msg='SLA plan added successfully';
+                $msg=sprintf(__('Successfully added %s'),
+                    __('a SLA plan'));
                 $_REQUEST['a']=null;
             }elseif(!$errors['err']){
-                $errors['err']='Unable to add SLA plan. Correct error(s) below and try again.';
+                $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                    __('this SLA plan'));
             }
             break;
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err'] = 'You must select at least one plan.';
+                $errors['err'] = sprintf(__('You must select at least %s.'),
+                    __('one SLA plan'));
             } else {
                 $count=count($_POST['ids']);
                 switch(strtolower($_POST['a'])) {
@@ -51,11 +58,14 @@ if($_POST){
 
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected SLA plans enabled';
+                                $msg = sprintf(__('Successfully enabled %s'),
+                                    _N('selected SLA plan', 'selected SLA plans', $count));
                             else
-                                $warn = "$num of $count selected SLA plans enabled";
+                                $warn = sprintf(__('%1$d of %2$d %3$s enabled'), $num, $count,
+                                    _N('selected SLA plan', 'selected SLA plans', $count));
                         } else {
-                            $errors['err'] = 'Unable to enable selected SLA plans.';
+                            $errors['err'] = sprintf(__('Unable to enable %s'),
+                                _N('selected SLA plan', 'selected SLA plans', $count));
                         }
                         break;
                     case 'disable':
@@ -63,11 +73,14 @@ if($_POST){
                             .' WHERE id IN ('.implode(',', db_input($_POST['ids'])).')';
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected SLA plans disabled';
+                                $msg = sprintf(__('Successfully disabled %s'),
+                                    _N('selected SLA plan', 'selected SLA plans', $count));
                             else
-                                $warn = "$num of $count selected SLA plans disabled";
+                                $warn = sprintf(__('%1$d of %2$d %3$s disabled'), $num, $count,
+                                    _N('selected SLA plan', 'selected SLA plans', $count));
                         } else {
-                            $errors['err'] = 'Unable to disable selected SLA plans';
+                            $errors['err'] = sprintf(__('Unable to disable %s'),
+                                _N('selected SLA plan', 'selected SLA plans', $count));
                         }
                         break;
                     case 'delete':
@@ -80,19 +93,22 @@ if($_POST){
                         }
 
                         if($i && $i==$count)
-                            $msg = 'Selected SLA plans deleted successfully';
+                            $msg = sprintf(__('Successfully deleted %s'),
+                                _N('selected SLA plan', 'selected SLA plans', $count));
                         elseif($i>0)
-                            $warn = "$i of $count selected SLA plans deleted";
+                            $warn = sprintf(__('%1$d of %2$d %3$s deleted'), $i, $count,
+                                _N('selected SLA plan', 'selected SLA plans', $count));
                         elseif(!$errors['err'])
-                            $errors['err'] = 'Unable to delete selected SLA plans';
+                            $errors['err'] = sprintf(__('Unable to delete %s'),
+                                _N('selected SLA plan', 'selected SLA plans', $count));
                         break;
                     default:
-                        $errors['err']='Unknown action - get technical help.';
+                        $errors['err']=__('Unknown action - get technical help.');
                 }
             }
             break;
         default:
-            $errors['err']='Unknown action/command';
+            $errors['err']=__('Unknown action');
             break;
     }
 }
diff --git a/scp/staff.inc.php b/scp/staff.inc.php
index e900ab0f89e7a9c0f2f51eab3efda01299746dd0..d4399c74b8fa94f9e049c84bf4be855c1d44df66 100644
--- a/scp/staff.inc.php
+++ b/scp/staff.inc.php
@@ -32,7 +32,6 @@ define('OSTSTAFFINC',TRUE);
 /* Tables used by staff only */
 define('KB_PREMADE_TABLE',TABLE_PREFIX.'kb_premade');
 
-
 /* include what is needed on staff control panel */
 
 require_once(INCLUDE_DIR.'class.staff.php');
@@ -58,15 +57,20 @@ 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
+TextDomain::configureForUser($thisstaff);
+
 //1) is the user Logged in for real && is staff.
 if (!$thisstaff || !$thisstaff->getId() || !$thisstaff->isValid()) {
     if (isset($_SESSION['_staff']['auth']['msg'])) {
         $msg = $_SESSION['_staff']['auth']['msg'];
         unset($_SESSION['_staff']['auth']['msg']);
     } elseif ($thisstaff && !$thisstaff->isValid())
-        $msg = 'Session timed out due to inactivity';
+        $msg = __('Session timed out due to inactivity');
     else
-        $msg = 'Authentication Required';
+        $msg = __('Authentication Required');
 
     staffLoginPage($msg);
     exit;
@@ -75,13 +79,13 @@ if (!$thisstaff || !$thisstaff->getId() || !$thisstaff->isValid()) {
 if(!$thisstaff->isAdmin()) {
     //Check for disabled staff or group!
     if(!$thisstaff->isactive() || !$thisstaff->isGroupActive()) {
-        staffLoginPage('Access Denied. Contact Admin');
+        staffLoginPage(__('Access Denied. Contact Admin'));
         exit;
     }
 
     //Staff are not allowed to login in offline mode!!
     if(!$ost->isSystemOnline() || $ost->isUpgradePending()) {
-        staffLoginPage('System Offline');
+        staffLoginPage(__('System Offline'));
         exit;
     }
 }
@@ -92,7 +96,7 @@ $thisstaff->refreshSession();
 /******* CSRF Protectin *************/
 // Enforce CSRF protection for POSTS
 if ($_POST  && !$ost->checkCSRFToken()) {
-    Http::response(400, 'Valid CSRF Token Required');
+    Http::response(400, __('Valid CSRF Token Required'));
     exit;
 }
 
@@ -106,20 +110,17 @@ $_SESSION['TZ_DST']=$thisstaff->observeDaylight();
 
 define('PAGE_LIMIT', $thisstaff->getPageLimit()?$thisstaff->getPageLimit():DEFAULT_PAGE_LIMIT);
 
-//Clear some vars. we use in all pages.
-$errors=array();
-$msg=$warn=$sysnotice='';
 $tabs=array();
 $submenu=array();
 $exempt = in_array(basename($_SERVER['SCRIPT_NAME']), array('logout.php', 'ajax.php', 'logs.php', 'upgrade.php'));
 
 if($ost->isUpgradePending() && !$exempt) {
-    $errors['err']=$sysnotice='System upgrade is pending <a href="upgrade.php">Upgrade Now</a>';
+    $errors['err']=$sysnotice=__('System upgrade is pending').' <a href="upgrade.php">'.__('Upgrade Now').'</a>';
     require('upgrade.php');
     exit;
 } elseif($cfg->isHelpDeskOffline()) {
-    $sysnotice='<strong>System is set to offline mode</strong> - Client interface is disabled and ONLY admins can access staff control panel.';
-    $sysnotice.=' <a href="settings.php">Enable</a>.';
+    $sysnotice='<strong>'.__('System is set to offline mode').'</strong> - '.__('Client interface is disabled and ONLY admins can access staff control panel.');
+    $sysnotice.=' <a href="settings.php">'.__('Enable').'</a>.';
 }
 
 if (!defined('AJAX_REQUEST'))
@@ -129,11 +130,11 @@ if (!defined('AJAX_REQUEST'))
 if($thisstaff->forcePasswdChange() && !$exempt) {
     # XXX: Call staffLoginPage() for AJAX and API requests _not_ to honor
     #      the request
-    $sysnotice = 'Password change required to continue';
+    $sysnotice = __('Password change required to continue');
     require('profile.php'); //profile.php must request this file as require_once to avoid problems.
     exit;
 }
 $ost->setWarning($sysnotice);
-$ost->setPageTitle('osTicket :: Staff Control Panel');
+$ost->setPageTitle(__('osTicket :: Staff Control Panel'));
 
 ?>
diff --git a/scp/staff.php b/scp/staff.php
index e66054b84485ffc0b6112d5f701b9787449bd90b..0ad72c2a1cc518be449f69902044dc860e712711 100644
--- a/scp/staff.php
+++ b/scp/staff.php
@@ -14,34 +14,39 @@
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
 require('admin.inc.php');
+
 $staff=null;
 if($_REQUEST['id'] && !($staff=Staff::lookup($_REQUEST['id'])))
-    $errors['err']='Unknown or invalid staff ID.';
+    $errors['err']=sprintf(__('%s: Unknown or invalid ID.'), __('agent'));
 
 if($_POST){
     switch(strtolower($_POST['do'])){
         case 'update':
             if(!$staff){
-                $errors['err']='Unknown or invalid staff.';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('agent'));
             }elseif($staff->update($_POST,$errors)){
-                $msg='Staff updated successfully';
+                $msg=sprintf(__('Successfully updated %s'),
+                    __('this agent'));
             }elseif(!$errors['err']){
-                $errors['err']='Unable to update staff. Correct any error(s) below and try again!';
+                $errors['err']=sprintf(__('Unable to update %s. Correct error(s) below and try again!'),
+                    __('this agent'));
             }
             break;
         case 'create':
             if(($id=Staff::create($_POST,$errors))){
-                $msg=Format::htmlchars($_POST['firstname']).' added successfully';
+                $msg=sprintf(__('Successfully added %s'),Format::htmlchars($_POST['firstname']));
                 $_REQUEST['a']=null;
             }elseif(!$errors['err']){
-                $errors['err']='Unable to add staff. Correct any error(s) below and try again.';
+                $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                    __('this agent'));
             }
             break;
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err'] = 'You must select at least one staff member.';
+                $errors['err'] = sprintf(__('You must select at least %s.'),
+                    __('one agent'));
             } elseif(in_array($thisstaff->getId(),$_POST['ids'])) {
-                $errors['err'] = 'You can not disable/delete yourself - you could be the only admin!';
+                $errors['err'] = __('You can not disable/delete yourself - you could be the only admin!');
             } else {
                 $count=count($_POST['ids']);
                 switch(strtolower($_POST['a'])) {
@@ -51,11 +56,14 @@ if($_POST){
 
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected staff activated';
+                                $msg = sprintf('Successfully activated %s',
+                                    _N('selected agent', 'selected agents', $count));
                             else
-                                $warn = "$num of $count selected staff activated";
+                                $warn = sprintf(__('%1$d of %2$d %3$s activated'), $num, $count,
+                                    _N('selected agent', 'selected agents', $count));
                         } else {
-                            $errors['err'] = 'Unable to activate selected staff';
+                            $errors['err'] = sprintf(__('Unable to activate %s'),
+                                _N('selected agent', 'selected agents', $count));
                         }
                         break;
                     case 'disable':
@@ -64,11 +72,14 @@ if($_POST){
 
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected staff disabled';
+                                $msg = sprintf('Successfully disabled %s',
+                                    _N('selected agent', 'selected agents', $count));
                             else
-                                $warn = "$num of $count selected staff disabled";
+                                $warn = sprintf(__('%1$d of %2$d %3$s disabled'), $num, $count,
+                                    _N('selected agent', 'selected agents', $count));
                         } else {
-                            $errors['err'] = 'Unable to disable selected staff';
+                            $errors['err'] = sprintf(__('Unable to disable %s'),
+                                _N('selected agent', 'selected agents', $count));
                         }
                         break;
                     case 'delete':
@@ -78,20 +89,23 @@ if($_POST){
                         }
 
                         if($i && $i==$count)
-                            $msg = 'Selected staff deleted successfully';
+                            $msg = sprintf(__('Successfully deleted %s'),
+                                _N('selected agent', 'selected agents', $count));
                         elseif($i>0)
-                            $warn = "$i of $count selected staff deleted";
+                            $warn = sprintf(__('%1$d of %2$d %3$s deleted'), $i, $count,
+                                _N('selected agent', 'selected agents', $count));
                         elseif(!$errors['err'])
-                            $errors['err'] = 'Unable to delete selected staff.';
+                            $errors['err'] = sprintf(__('Unable to delete %s'),
+                                _N('selected agent', 'selected agents', $count));
                         break;
                     default:
-                        $errors['err'] = 'Unknown action. Get technical help!';
+                        $errors['err'] = __('Unknown action - get technical help.');
                 }
 
             }
             break;
         default:
-            $errors['err']='Unknown action/command';
+            $errors['err']=__('Unknown action');
             break;
     }
 }
diff --git a/scp/teams.php b/scp/teams.php
index 750ac9ca43f086a607635ea863e33bdc6d3ee51c..215e9fb8788154f34ed6947a05bbf051292e690a 100644
--- a/scp/teams.php
+++ b/scp/teams.php
@@ -14,32 +14,36 @@
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
 require('admin.inc.php');
+
 $team=null;
 if($_REQUEST['id'] && !($team=Team::lookup($_REQUEST['id'])))
-    $errors['err']='Unknown or invalid team ID.';
+    $errors['err']=sprintf(__('%s: Unknown or invalid'), __('team'));
 
 if($_POST){
     switch(strtolower($_POST['do'])){
         case 'update':
             if(!$team){
-                $errors['err']='Unknown or invalid team.';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('team'));
             }elseif($team->update($_POST,$errors)){
-                $msg='Team updated successfully';
+                $msg=sprintf(__('Successfully updated %s'),
+                    __('this team'));
             }elseif(!$errors['err']){
-                $errors['err']='Unable to update team. Correct any error(s) below and try again!';
+                $errors['err']=sprintf(__('Unable to update %s. Correct any error(s) below and try again.'),
+                    __('this team'));
             }
             break;
         case 'create':
             if(($id=Team::create($_POST,$errors))){
-                $msg=Format::htmlchars($_POST['team']).' added successfully';
+                $msg=sprintf(__('Successfully added %s'),Format::htmlchars($_POST['team']));
                 $_REQUEST['a']=null;
             }elseif(!$errors['err']){
-                $errors['err']='Unable to add team. Correct any error(s) below and try again.';
+                $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                    __('this team'));
             }
             break;
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err']='You must select at least one team.';
+                $errors['err']=sprintf(__('You must select at least %s.'), __('one team'));
             } else {
                 $count=count($_POST['ids']);
                 switch(strtolower($_POST['a'])) {
@@ -49,11 +53,14 @@ if($_POST){
 
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected teams activated';
+                                $msg = sprintf(__('Successfully activated %s'),
+                                    _N('selected team', 'selected teams', $count));
                             else
-                                $warn = "$num of $count selected teams activated";
+                                $warn = sprintf(__('%1$d of %2$d %3$s activated'), $num, $count,
+                                    _N('selected team', 'selected teams', $count));
                         } else {
-                            $errors['err'] = 'Unable to activate selected teams';
+                            $errors['err'] = sprintf(__('Unable to activate %s'),
+                                _N('selected team', 'selected teams', $count));
                         }
                         break;
                     case 'disable':
@@ -62,11 +69,14 @@ if($_POST){
 
                         if(db_query($sql) && ($num=db_affected_rows())) {
                             if($num==$count)
-                                $msg = 'Selected teams disabled';
+                                $msg = sprintf(__('Successfully disabled %s'),
+                                    _N('selected team', 'selected teams', $count));
                             else
-                                $warn = "$num of $count selected teams disabled";
+                                $warn = sprintf(__('%1$d of %2$d %3$s disabled'), $num, $count,
+                                    _N('selected team', 'selected teams', $count));
                         } else {
-                            $errors['err'] = 'Unable to disable selected teams';
+                            $errors['err'] = sprintf(__('Unable to disable %s'),
+                                _N('selected team', 'selected teams', $count));
                         }
                         break;
                     case 'delete':
@@ -75,19 +85,22 @@ if($_POST){
                                 $i++;
                         }
                         if($i && $i==$count)
-                            $msg = 'Selected teams deleted successfully';
+                            $msg = sprintf(__('Successfully deleted %s'),
+                                _N('selected team', 'selected teams', $count));
                         elseif($i>0)
-                            $warn = "$i of $count selected teams deleted";
+                            $warn = sprintf(__('%1$d of %2$d %3$s deleted'), $i, $count,
+                                _N('selected team', 'selected teams', $count));
                         elseif(!$errors['err'])
-                            $errors['err'] = 'Unable to delete selected teams';
+                            $errors['err'] = sprintf(__('Unable to delete %s'),
+                                _N('selected team', 'selected teams', $count));
                         break;
                     default:
-                        $errors['err'] = 'Unknown action. Get technical help!';
+                        $errors['err'] = __('Unknown action - get technical help.');
                 }
             }
             break;
         default:
-            $errors['err']='Unknown action/command';
+            $errors['err']=__('Unknown action');
             break;
     }
 }
diff --git a/scp/templates.php b/scp/templates.php
index 809e425618314c6f9568c9c41e3a253ac648f431..34f8b23ea6493840c89cf1d37bf9815dc0442490 100644
--- a/scp/templates.php
+++ b/scp/templates.php
@@ -15,13 +15,14 @@
 **********************************************************************/
 require('admin.inc.php');
 include_once(INCLUDE_DIR.'class.template.php');
+
 $template=null;
 if($_REQUEST['tpl_id'] &&
         !($template=EmailTemplateGroup::lookup($_REQUEST['tpl_id'])))
-    $errors['err']='Unknown or invalid template group ID.';
+    $errors['err']=sprintf(__('%s: Unknown or invalid'), __('template set'));
 elseif($_REQUEST['id'] &&
         !($template=EmailTemplate::lookup($_REQUEST['id'])))
-    $errors['err']='Unknown or invalid template ID.';
+    $errors['err']=sprintf(__('%s: Unknown or invalid %s'), __('template'));
 elseif($_REQUEST['default_for']) {
     $sql = 'SELECT id FROM '.EMAIL_TEMPLATE_TABLE
         .' WHERE tpl_id='.db_input($cfg->getDefaultTemplateId())
@@ -34,50 +35,59 @@ if($_POST){
     switch(strtolower($_POST['do'])){
         case 'updatetpl':
             if(!$template){
-                $errors['err']='Unknown or invalid template';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'),
+                    __('message template'));
             }elseif($template->update($_POST,$errors)){
-                $msg='Message template updated successfully';
+                $msg=sprintf(__('Successfully updated %s'),
+                    __('this message template'));
                 // Drop drafts for this template for ALL users
                 Draft::deleteForNamespace('tpl.'.$template->getCodeName()
                     .'.'.$template->getTplId());
             }elseif(!$errors['err']){
-                $errors['err']='Error updating message template. Try again!';
+                $errors['err']=sprintf(__('Error updating %s. Try again!'),
+                    __('this template'));
             }
             break;
         case 'implement':
             if(!$template){
-                $errors['err']='Unknown or invalid template';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('template set'));
             }elseif($new = EmailTemplate::add($_POST,$errors)){
                 $template = $new;
-                $msg='Message template updated successfully';
+                $msg=sprintf(__('Successfully updated %s'), __('this message template'));
                 // Drop drafts for this user for this template
                 Draft::deleteForNamespace('tpl.'.$new->getCodeName()
                     .$new->getTplId(), $thisstaff->getId());
             }elseif(!$errors['err']){
-                $errors['err']='Error updating message template. Try again!';
+                $errors['err']=sprintf(__('Error updating %s. Try again!'),
+                    __('this message template'));
             }
             break;
         case 'update':
             if(!$template){
-                $errors['err']='Unknown or invalid template';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), __('template set'));
             }elseif($template->update($_POST,$errors)){
-                $msg='Template updated successfully';
+                $msg=sprintf(__('Successfully updated %s'),
+                    mb_convert_case(__('this message template'), MB_CASE_TITLE));
             }elseif(!$errors['err']){
-                $errors['err']='Error updating template. Try again!';
+                $errors['err']=sprintf(__('Error updating %s. Try again!'),
+                    __('this message template'));
             }
             break;
         case 'add':
             if(($new=EmailTemplateGroup::add($_POST,$errors))){
                 $template=$new;
-                $msg='Template added successfully';
+                $msg=sprintf(__('Successfully added %s'),
+                    mb_convert_case(__('a template set'), MB_CASE_TITLE));
                 $_REQUEST['a']=null;
             }elseif(!$errors['err']){
-                $errors['err']='Unable to add template. Correct error(s) below and try again.';
+                $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                    __('this template set'));
             }
             break;
         case 'mass_process':
             if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err']='You must select at least one template to process.';
+                $errors['err']=sprintf(__('You must select at least %s to process.'),
+                    __('one template set'));
             } else {
                 $count=count($_POST['ids']);
                 switch(strtolower($_POST['a'])) {
@@ -86,11 +96,14 @@ if($_POST){
                             .' WHERE tpl_id IN ('.implode(',', db_input($_POST['ids'])).')';
                         if(db_query($sql) && ($num=db_affected_rows())){
                             if($num==$count)
-                                $msg = 'Selected templates enabled';
+                                $msg = sprintf(__('Successfully enabled %s'),
+                                    _N('selected template set', 'selected template sets', $count));
                             else
-                                $warn = "$num of $count selected templates enabled";
+                                $warn = sprintf(__('%1$d of %2$d %3$s enabled'), $num, $count,
+                                    _N('selected template set', 'selected template sets', $count));
                         } else {
-                            $errors['err'] = 'Unable to enable selected templates';
+                            $errors['err'] = sprintf(__('Unable to enable %s'),
+                                _N('selected template set', 'selected template sets', $count));
                         }
                         break;
                     case 'disable':
@@ -100,11 +113,16 @@ if($_POST){
                                 $i++;
                         }
                         if($i && $i==$count)
-                            $msg = 'Selected templates disabled';
+                            $msg = sprintf(__('Successfully disabled %s'),
+                                _N('selected template set', 'selected template sets', $count));
                         elseif($i)
-                            $warn = "$i of $count selected templates disabled (in-use templates can't be disabled)";
+                            $warn = sprintf(__('%1$d of %2$d %3$s disabled'), $i, $count,
+                                _N('selected template set', 'selected template sets', $count))
+                               .' '.__('(in-use and default template sets cannot be disabled)');
                         else
-                            $errors['err'] = "Unable to disable selected templates (in-use or default template can't be disabled)";
+                            $errors['err'] = sprintf(__("Unable to disable %s"),
+                                _N('selected template set', 'selected template sets', $count))
+                               .' '.__('(in-use and default template sets cannot be disabled)');
                         break;
                     case 'delete':
                         $i=0;
@@ -114,19 +132,22 @@ if($_POST){
                         }
 
                         if($i && $i==$count)
-                            $msg = 'Selected templates deleted successfully';
+                            $msg = sprintf(__('Successfully deleted %s'),
+                                _N('selected template set', 'selected template sets', $count));
                         elseif($i>0)
-                            $warn = "$i of $count selected templates deleted";
+                            $warn = sprintf(__('%1$d of %2$d %3$s deleted'), $i, $count,
+                                _N('selected template set', 'selected template sets', $count));
                         elseif(!$errors['err'])
-                            $errors['err'] = 'Unable to delete selected templates';
+                            $errors['err'] = sprintf(__('Unable to delete %s'),
+                                _N('selected template set', 'selected template sets', $count));
                         break;
                     default:
-                        $errors['err']='Unknown template action';
+                        $errors['err']=__('Unknown action - get technical help.');
                 }
             }
             break;
         default:
-            $errors['err']='Unknown action';
+            $errors['err']=__('Unknown action');
             break;
     }
 }
diff --git a/scp/tickets.php b/scp/tickets.php
index 6342dcaa94a92c232da8c0efe8ede8df0a59bab1..33300cb44131eaa48136d958f1160478fa20bd28 100644
--- a/scp/tickets.php
+++ b/scp/tickets.php
@@ -21,16 +21,16 @@ require_once(INCLUDE_DIR.'class.filter.php');
 require_once(INCLUDE_DIR.'class.canned.php');
 require_once(INCLUDE_DIR.'class.json.php');
 require_once(INCLUDE_DIR.'class.dynamic_forms.php');
-
+require_once(INCLUDE_DIR.'class.export.php');       // For paper sizes
 
 $page='';
 $ticket = $user = null; //clean start.
 //LOCKDOWN...See if the id provided is actually valid and if the user has access.
 if($_REQUEST['id']) {
     if(!($ticket=Ticket::lookup($_REQUEST['id'])))
-         $errors['err']='Unknown or invalid ticket ID';
+         $errors['err']=sprintf(__('%s: Unknown or invalid ID.'), __('ticket'));
     elseif(!$ticket->checkStaffAccess($thisstaff)) {
-        $errors['err']='Access denied. Contact admin if you believe this is in error';
+        $errors['err']=__('Access denied. Contact admin if you believe this is in error');
         $ticket=null; //Clear ticket obj.
     }
 }
@@ -39,6 +39,18 @@ if($_REQUEST['id']) {
 if ($_REQUEST['uid'])
     $user = User::lookup($_REQUEST['uid']);
 
+// Configure form for file uploads
+$response_form = new Form(array(
+    'attachments' => new FileUploadField(array('id'=>'attach',
+        'name'=>'attach:response',
+        'configuration' => array('extensions'=>'')))
+));
+$note_form = new Form(array(
+    'attachments' => new FileUploadField(array('id'=>'attach',
+        'name'=>'attach:note',
+        'configuration' => array('extensions'=>'')))
+));
+
 //At this stage we know the access status. we can process the post.
 if($_POST && !$errors):
 
@@ -46,140 +58,143 @@ if($_POST && !$errors):
         //More coffee please.
         $errors=array();
         $lock=$ticket->getLock(); //Ticket lock if any
-        $statusKeys=array('open'=>'Open','Reopen'=>'Open','Close'=>'Closed');
         switch(strtolower($_POST['a'])):
         case 'reply':
             if(!$thisstaff->canPostReply())
-                $errors['err'] = 'Action denied. Contact admin for access';
+                $errors['err'] = __('Action denied. Contact admin for access');
             else {
 
                 if(!$_POST['response'])
-                    $errors['response']='Response required';
+                    $errors['response']=__('Response required');
                 //Use locks to avoid double replies
                 if($lock && $lock->getStaffId()!=$thisstaff->getId())
-                    $errors['err']='Action Denied. Ticket is locked by someone else!';
+                    $errors['err']=__('Action Denied. Ticket is locked by someone else!');
 
                 //Make sure the email is not banned
                 if(!$errors['err'] && TicketFilter::isBanned($ticket->getEmail()))
-                    $errors['err']='Email is in banlist. Must be removed to reply.';
+                    $errors['err']=__('Email is in banlist. Must be removed to reply.');
             }
 
-            $wasOpen =($ticket->isOpen());
-
             //If no error...do the do.
             $vars = $_POST;
-            if(!$errors && $_FILES['attachments'])
-                $vars['files'] = AttachmentFile::format($_FILES['attachments']);
+            $vars['cannedattachments'] = $response_form->getField('attachments')->getClean();
 
             if(!$errors && ($response=$ticket->postReply($vars, $errors, $_POST['emailreply']))) {
-                $msg='Reply posted successfully';
-                $ticket->reload();
+                $msg = sprintf(__('%s: Reply posted successfully'),
+                        sprintf(__('Ticket #%s'),
+                            sprintf('<a href="tickets.php?id=%d"><b>%s</b></a>',
+                                $ticket->getId(), $ticket->getNumber()))
+                        );
 
-                if($ticket->isClosed() && $wasOpen)
-                    $ticket=null;
-                else
-                    // Still open -- cleanup response draft for this user
-                    Draft::deleteForNamespace(
-                        'ticket.response.' . $ticket->getId(),
-                        $thisstaff->getId());
+                // Clear attachment list
+                $response_form->setSource(array());
+                $response_form->getField('attachments')->reset();
+
+                // Remove staff's locks
+                TicketLock::removeStaffLocks($thisstaff->getId(),
+                        $ticket->getId());
+
+                // Cleanup response draft for this user
+                Draft::deleteForNamespace(
+                    'ticket.response.' . $ticket->getId(),
+                    $thisstaff->getId());
+
+                // Go back to the ticket listing page on reply
+                $ticket = null;
 
             } elseif(!$errors['err']) {
-                $errors['err']='Unable to post the reply. Correct the errors below and try again!';
+                $errors['err']=__('Unable to post the reply. Correct the errors below and try again!');
             }
             break;
         case 'transfer': /** Transfer ticket **/
             //Check permission
             if(!$thisstaff->canTransferTickets())
-                $errors['err']=$errors['transfer'] = 'Action Denied. You are not allowed to transfer tickets.';
+                $errors['err']=$errors['transfer'] = __('Action Denied. You are not allowed to transfer tickets.');
             else {
 
                 //Check target dept.
                 if(!$_POST['deptId'])
-                    $errors['deptId'] = 'Select department';
+                    $errors['deptId'] = __('Select department');
                 elseif($_POST['deptId']==$ticket->getDeptId())
-                    $errors['deptId'] = 'Ticket already in the department';
+                    $errors['deptId'] = __('Ticket already in the department');
                 elseif(!($dept=Dept::lookup($_POST['deptId'])))
-                    $errors['deptId'] = 'Unknown or invalid department';
+                    $errors['deptId'] = __('Unknown or invalid department');
 
                 //Transfer message - required.
                 if(!$_POST['transfer_comments'])
-                    $errors['transfer_comments'] = 'Transfer comments required';
+                    $errors['transfer_comments'] = __('Transfer comments required');
                 elseif(strlen($_POST['transfer_comments'])<5)
-                    $errors['transfer_comments'] = 'Transfer comments too short!';
+                    $errors['transfer_comments'] = __('Transfer comments too short!');
 
                 //If no errors - them attempt the transfer.
                 if(!$errors && $ticket->transfer($_POST['deptId'], $_POST['transfer_comments'])) {
-                    $msg = 'Ticket transferred successfully to '.$ticket->getDeptName();
+                    $msg = sprintf(__('Ticket transferred successfully to %s'),$ticket->getDeptName());
                     //Check to make sure the staff still has access to the ticket
                     if(!$ticket->checkStaffAccess($thisstaff))
                         $ticket=null;
 
                 } elseif(!$errors['transfer']) {
-                    $errors['err'] = 'Unable to complete the ticket transfer';
-                    $errors['transfer']='Correct the error(s) below and try again!';
+                    $errors['err'] = __('Unable to complete the ticket transfer');
+                    $errors['transfer']=__('Correct the error(s) below and try again!');
                 }
             }
             break;
         case 'assign':
 
              if(!$thisstaff->canAssignTickets())
-                 $errors['err']=$errors['assign'] = 'Action Denied. You are not allowed to assign/reassign tickets.';
+                 $errors['err']=$errors['assign'] = __('Action Denied. You are not allowed to assign/reassign tickets.');
              else {
 
                  $id = preg_replace("/[^0-9]/", "",$_POST['assignId']);
                  $claim = (is_numeric($_POST['assignId']) && $_POST['assignId']==$thisstaff->getId());
 
                  if(!$_POST['assignId'] || !$id)
-                     $errors['assignId'] = 'Select assignee';
+                     $errors['assignId'] = __('Select assignee');
                  elseif($_POST['assignId'][0]!='s' && $_POST['assignId'][0]!='t' && !$claim)
-                     $errors['assignId']='Invalid assignee ID - get technical support';
+                     $errors['assignId']=__('Invalid assignee ID - get technical support');
                  elseif($ticket->isAssigned()) {
                      if($_POST['assignId'][0]=='s' && $id==$ticket->getStaffId())
-                         $errors['assignId']='Ticket already assigned to the staff.';
+                         $errors['assignId']=__('Ticket already assigned to the agent.');
                      elseif($_POST['assignId'][0]=='t' && $id==$ticket->getTeamId())
-                         $errors['assignId']='Ticket already assigned to the team.';
+                         $errors['assignId']=__('Ticket already assigned to the team.');
                  }
 
                  //Comments are not required on self-assignment (claim)
                  if($claim && !$_POST['assign_comments'])
-                     $_POST['assign_comments'] = 'Ticket claimed by '.$thisstaff->getName();
+                     $_POST['assign_comments'] = sprintf(__('Ticket claimed by %s'),$thisstaff->getName());
                  elseif(!$_POST['assign_comments'])
-                     $errors['assign_comments'] = 'Assignment comments required';
+                     $errors['assign_comments'] = __('Assignment comments required');
                  elseif(strlen($_POST['assign_comments'])<5)
-                         $errors['assign_comments'] = 'Comment too short';
+                         $errors['assign_comments'] = __('Comment too short');
 
                  if(!$errors && $ticket->assign($_POST['assignId'], $_POST['assign_comments'], !$claim)) {
                      if($claim) {
-                         $msg = 'Ticket is NOW assigned to you!';
+                         $msg = __('Ticket is NOW assigned to you!');
                      } else {
-                         $msg='Ticket assigned successfully to '.$ticket->getAssigned();
+                         $msg=sprintf(__('Ticket assigned successfully to %s'), $ticket->getAssigned());
                          TicketLock::removeStaffLocks($thisstaff->getId(), $ticket->getId());
                          $ticket=null;
                      }
                  } elseif(!$errors['assign']) {
-                     $errors['err'] = 'Unable to complete the ticket assignment';
-                     $errors['assign'] = 'Correct the error(s) below and try again!';
+                     $errors['err'] = __('Unable to complete the ticket assignment');
+                     $errors['assign'] = __('Correct the error(s) below and try again!');
                  }
              }
             break;
         case 'postnote': /* Post Internal Note */
-            //Make sure the staff can set desired state
-            if($_POST['state']) {
-                if($_POST['state']=='closed' && !$thisstaff->canCloseTickets())
-                    $errors['state'] = "You don't have permission to close tickets";
-                elseif(in_array($_POST['state'], array('overdue', 'notdue', 'unassigned'))
-                        && (!($dept=$ticket->getDept()) || !$dept->isManager($thisstaff)))
-                    $errors['state'] = "You don't have permission to set the state";
-            }
-
             $vars = $_POST;
-            if($_FILES['attachments'])
-                $vars['files'] = AttachmentFile::format($_FILES['attachments']);
+            $attachments = $note_form->getField('attachments')->getClean();
+            $vars['cannedattachments'] = array_merge(
+                $vars['cannedattachments'] ?: array(), $attachments);
 
             $wasOpen = ($ticket->isOpen());
             if(($note=$ticket->postNote($vars, $errors, $thisstaff))) {
 
-                $msg='Internal note posted successfully';
+                $msg=__('Internal note posted successfully');
+                // Clear attachment list
+                $note_form->setSource(array());
+                $note_form->getField('attachments')->reset();
+
                 if($wasOpen && $ticket->isClosed())
                     $ticket = null; //Going back to main listing.
                 else
@@ -190,317 +205,157 @@ if($_POST && !$errors):
             } else {
 
                 if(!$errors['err'])
-                    $errors['err'] = 'Unable to post internal note - missing or invalid data.';
+                    $errors['err'] = __('Unable to post internal note - missing or invalid data.');
 
-                $errors['postnote'] = 'Unable to post the note. Correct the error(s) below and try again!';
+                $errors['postnote'] = __('Unable to post the note. Correct the error(s) below and try again!');
             }
             break;
         case 'edit':
         case 'update':
-            $forms=DynamicFormEntry::forTicket($ticket->getId());
-            foreach ($forms as $form) {
-                // Don't validate deleted forms
-                if (!in_array($form->getId(), $_POST['forms']))
-                    continue;
-                $form->setSource($_POST);
-                if (!$form->isValid())
-                    $errors = array_merge($errors, $form->errors());
-            }
             if(!$ticket || !$thisstaff->canEditTickets())
-                $errors['err']='Permission Denied. You are not allowed to edit tickets';
+                $errors['err']=__('Permission Denied. You are not allowed to edit tickets');
             elseif($ticket->update($_POST,$errors)) {
-                $msg='Ticket updated successfully';
+                $msg=__('Ticket updated successfully');
                 $_REQUEST['a'] = null; //Clear edit action - going back to view.
                 //Check to make sure the staff STILL has access post-update (e.g dept change).
-                foreach ($forms as $f) {
-                    // Drop deleted forms
-                    $idx = array_search($f->getId(), $_POST['forms']);
-                    if ($idx === false) {
-                        $f->delete();
-                    }
-                    else {
-                        $f->set('sort', $idx);
-                        $f->save();
-                    }
-                }
                 if(!$ticket->checkStaffAccess($thisstaff))
                     $ticket=null;
             } elseif(!$errors['err']) {
-                $errors['err']='Unable to update the ticket. Correct the errors below and try again!';
+                $errors['err']=__('Unable to update the ticket. Correct the errors below and try again!');
             }
             break;
         case 'process':
             switch(strtolower($_POST['do'])):
-                case 'close':
-                    if(!$thisstaff->canCloseTickets()) {
-                        $errors['err'] = 'Permission Denied. You are not allowed to close tickets.';
-                    } elseif($ticket->isClosed()) {
-                        $errors['err'] = 'Ticket is already closed!';
-                    } elseif($ticket->close()) {
-                        $msg='Ticket #'.$ticket->getNumber().' status set to CLOSED';
-                        //Log internal note
-                        if($_POST['ticket_status_notes'])
-                            $note = $_POST['ticket_status_notes'];
-                        else
-                            $note='Ticket closed (without comments)';
-
-                        $ticket->logNote('Ticket Closed', $note, $thisstaff);
-
-                        //Going back to main listing.
-                        TicketLock::removeStaffLocks($thisstaff->getId(), $ticket->getId());
-                        $page=$ticket=null;
-
-                    } else {
-                        $errors['err']='Problems closing the ticket. Try again';
-                    }
-                    break;
-                case 'reopen':
-                    //if staff can close or create tickets ...then assume they can reopen.
-                    if(!$thisstaff->canCloseTickets() && !$thisstaff->canCreateTickets()) {
-                        $errors['err']='Permission Denied. You are not allowed to reopen tickets.';
-                    } elseif($ticket->isOpen()) {
-                        $errors['err'] = 'Ticket is already open!';
-                    } elseif($ticket->reopen()) {
-                        $msg='Ticket REOPENED';
-
-                        if($_POST['ticket_status_notes'])
-                            $note = $_POST['ticket_status_notes'];
-                        else
-                            $note='Ticket reopened (without comments)';
-
-                        $ticket->logNote('Ticket Reopened', $note, $thisstaff);
-
-                    } else {
-                        $errors['err']='Problems reopening the ticket. Try again';
-                    }
-                    break;
                 case 'release':
                     if(!$ticket->isAssigned() || !($assigned=$ticket->getAssigned())) {
-                        $errors['err'] = 'Ticket is not assigned!';
+                        $errors['err'] = __('Ticket is not assigned!');
                     } elseif($ticket->release()) {
-                        $msg='Ticket released (unassigned) from '.$assigned;
-                        $ticket->logActivity('Ticket unassigned',$msg.' by '.$thisstaff->getName());
+                        $msg=sprintf(__(
+                            /* 1$ is the current assignee, 2$ is the agent removing the assignment */
+                            'Ticket released (unassigned) from %1$s by %2$s'),
+                            $assigned, $thisstaff->getName());
+                        $ticket->logActivity(__('Ticket unassigned'),$msg);
                     } else {
-                        $errors['err'] = 'Problems releasing the ticket. Try again';
+                        $errors['err'] = __('Problems releasing the ticket. Try again');
                     }
                     break;
                 case 'claim':
                     if(!$thisstaff->canAssignTickets()) {
-                        $errors['err'] = 'Permission Denied. You are not allowed to assign/claim tickets.';
+                        $errors['err'] = __('Permission Denied. You are not allowed to assign/claim tickets.');
                     } elseif(!$ticket->isOpen()) {
-                        $errors['err'] = 'Only open tickets can be assigned';
+                        $errors['err'] = __('Only open tickets can be assigned');
                     } elseif($ticket->isAssigned()) {
-                        $errors['err'] = 'Ticket is already assigned to '.$ticket->getAssigned();
-                    } elseif($ticket->assignToStaff($thisstaff->getId(), ('Ticket claimed by '.$thisstaff->getName()), false)) {
-                        $msg = 'Ticket is now assigned to you!';
+                        $errors['err'] = sprintf(__('Ticket is already assigned to %s'),$ticket->getAssigned());
+                    } elseif($ticket->assignToStaff($thisstaff->getId(), (sprintf(__('Ticket claimed by %s'),$thisstaff->getName())), false)) {
+                        $msg = __('Ticket is now assigned to you!');
                     } else {
-                        $errors['err'] = 'Problems assigning the ticket. Try again';
+                        $errors['err'] = __('Problems assigning the ticket. Try again');
                     }
                     break;
                 case 'overdue':
                     $dept = $ticket->getDept();
                     if(!$dept || !$dept->isManager($thisstaff)) {
-                        $errors['err']='Permission Denied. You are not allowed to flag tickets overdue';
+                        $errors['err']=__('Permission Denied. You are not allowed to flag tickets overdue');
                     } elseif($ticket->markOverdue()) {
-                        $msg='Ticket flagged as overdue';
-                        $ticket->logActivity('Ticket Marked Overdue',($msg.' by '.$thisstaff->getName()));
+                        $msg=sprintf(__('Ticket flagged as overdue by %s'),$thisstaff->getName());
+                        $ticket->logActivity(__('Ticket Marked Overdue'),$msg);
                     } else {
-                        $errors['err']='Problems marking the the ticket overdue. Try again';
+                        $errors['err']=__('Problems marking the the ticket overdue. Try again');
                     }
                     break;
                 case 'answered':
                     $dept = $ticket->getDept();
                     if(!$dept || !$dept->isManager($thisstaff)) {
-                        $errors['err']='Permission Denied. You are not allowed to flag tickets';
+                        $errors['err']=__('Permission Denied. You are not allowed to flag tickets');
                     } elseif($ticket->markAnswered()) {
-                        $msg='Ticket flagged as answered';
-                        $ticket->logActivity('Ticket Marked Answered',($msg.' by '.$thisstaff->getName()));
+                        $msg=sprintf(__('Ticket flagged as answered by %s'),$thisstaff->getName());
+                        $ticket->logActivity(__('Ticket Marked Answered'),$msg);
                     } else {
-                        $errors['err']='Problems marking the the ticket answered. Try again';
+                        $errors['err']=__('Problems marking the the ticket answered. Try again');
                     }
                     break;
                 case 'unanswered':
                     $dept = $ticket->getDept();
                     if(!$dept || !$dept->isManager($thisstaff)) {
-                        $errors['err']='Permission Denied. You are not allowed to flag tickets';
+                        $errors['err']=__('Permission Denied. You are not allowed to flag tickets');
                     } elseif($ticket->markUnAnswered()) {
-                        $msg='Ticket flagged as unanswered';
-                        $ticket->logActivity('Ticket Marked Unanswered',($msg.' by '.$thisstaff->getName()));
+                        $msg=sprintf(__('Ticket flagged as unanswered by %s'),$thisstaff->getName());
+                        $ticket->logActivity(__('Ticket Marked Unanswered'),$msg);
                     } else {
-                        $errors['err']='Problems marking the the ticket unanswered. Try again';
+                        $errors['err']=__('Problems marking the ticket unanswered. Try again');
                     }
                     break;
                 case 'banemail':
                     if(!$thisstaff->canBanEmails()) {
-                        $errors['err']='Permission Denied. You are not allowed to ban emails';
+                        $errors['err']=__('Permission Denied. You are not allowed to ban emails');
                     } elseif(BanList::includes($ticket->getEmail())) {
-                        $errors['err']='Email already in banlist';
+                        $errors['err']=__('Email already in banlist');
                     } elseif(Banlist::add($ticket->getEmail(),$thisstaff->getName())) {
-                        $msg='Email ('.$ticket->getEmail().') added to banlist';
+                        $msg=sprintf(__('Email %s added to banlist'),$ticket->getEmail());
                     } else {
-                        $errors['err']='Unable to add the email to banlist';
+                        $errors['err']=__('Unable to add the email to banlist');
                     }
                     break;
                 case 'unbanemail':
                     if(!$thisstaff->canBanEmails()) {
-                        $errors['err'] = 'Permission Denied. You are not allowed to remove emails from banlist.';
+                        $errors['err'] = __('Permission Denied. You are not allowed to remove emails from banlist.');
                     } elseif(Banlist::remove($ticket->getEmail())) {
-                        $msg = 'Email removed from banlist';
+                        $msg = __('Email removed from banlist');
                     } elseif(!BanList::includes($ticket->getEmail())) {
-                        $warn = 'Email is not in the banlist';
+                        $warn = __('Email is not in the banlist');
                     } else {
-                        $errors['err']='Unable to remove the email from banlist. Try again.';
+                        $errors['err']=__('Unable to remove the email from banlist. Try again.');
                     }
                     break;
                 case 'changeuser':
                     if (!$thisstaff->canEditTickets()) {
-                        $errors['err'] = 'Permission Denied. You are not allowed to EDIT tickets!!';
+                        $errors['err']=__('Permission Denied. You are not allowed to edit tickets');
                     } elseif (!$_POST['user_id'] || !($user=User::lookup($_POST['user_id']))) {
-                        $errors['err'] = 'Unknown user selected!';
+                        $errors['err'] = __('Unknown user selected');
                     } elseif ($ticket->changeOwner($user)) {
-                        $msg = 'Ticket ownership changed to ' . Format::htmlchars($user->getName());
+                        $msg = sprintf(__('Ticket ownership changed to %s'),
+                            Format::htmlchars($user->getName()));
                     } else {
-                        $errors['err'] = 'Unable to change tiket ownership. Try again';
-                    }
-                    break;
-                case 'delete': // Dude what are you trying to hide? bad customer support??
-                    if(!$thisstaff->canDeleteTickets()) {
-                        $errors['err']='Permission Denied. You are not allowed to DELETE tickets!!';
-                    } elseif($ticket->delete()) {
-                        $msg='Ticket #'.$ticket->getNumber().' deleted successfully';
-                        //Log a debug note
-                        $ost->logDebug('Ticket #'.$ticket->getNumber().' deleted',
-                                sprintf('Ticket #%s deleted by %s',
-                                    $ticket->getNumber(), $thisstaff->getName())
-                                );
-                        $ticket=null; //clear the object.
-                    } else {
-                        $errors['err']='Problems deleting the ticket. Try again';
+                        $errors['err'] = __('Unable to change ticket ownership. Try again');
                     }
                     break;
                 default:
-                    $errors['err']='You must select action to perform';
+                    $errors['err']=__('You must select action to perform');
             endswitch;
             break;
         default:
-            $errors['err']='Unknown action';
+            $errors['err']=__('Unknown action');
         endswitch;
         if($ticket && is_object($ticket))
             $ticket->reload();//Reload ticket info following post processing
     }elseif($_POST['a']) {
 
         switch($_POST['a']) {
-            case 'mass_process':
-                if(!$thisstaff->canManageTickets())
-                    $errors['err']='You do not have permission to mass manage tickets. Contact admin for such access';
-                elseif(!$_POST['tids'] || !is_array($_POST['tids']))
-                    $errors['err']='No tickets selected. You must select at least one ticket.';
-                else {
-                    $count=count($_POST['tids']);
-                    $i = 0;
-                    switch(strtolower($_POST['do'])) {
-                        case 'reopen':
-                            if($thisstaff->canCloseTickets() || $thisstaff->canCreateTickets()) {
-                                $note='Ticket reopened by '.$thisstaff->getName();
-                                foreach($_POST['tids'] as $k=>$v) {
-                                    if(($t=Ticket::lookup($v)) && $t->isClosed() && @$t->reopen()) {
-                                        $i++;
-                                        $t->logNote('Ticket Reopened', $note, $thisstaff);
-                                    }
-                                }
-
-                                if($i==$count)
-                                    $msg = "Selected tickets ($i) reopened successfully";
-                                elseif($i)
-                                    $warn = "$i of $count selected tickets reopened";
-                                else
-                                    $errors['err'] = 'Unable to reopen selected tickets';
-                            } else {
-                                $errors['err'] = 'You do not have permission to reopen tickets';
-                            }
-                            break;
-                        case 'close':
-                            if($thisstaff->canCloseTickets()) {
-                                $note='Ticket closed without response by '.$thisstaff->getName();
-                                foreach($_POST['tids'] as $k=>$v) {
-                                    if(($t=Ticket::lookup($v)) && $t->isOpen() && @$t->close()) {
-                                        $i++;
-                                        $t->logNote('Ticket Closed', $note, $thisstaff);
-                                    }
-                                }
-
-                                if($i==$count)
-                                    $msg ="Selected tickets ($i) closed succesfully";
-                                elseif($i)
-                                    $warn = "$i of $count selected tickets closed";
-                                else
-                                    $errors['err'] = 'Unable to close selected tickets';
-                            } else {
-                                $errors['err'] = 'You do not have permission to close tickets';
-                            }
-                            break;
-                        case 'mark_overdue':
-                            $note='Ticket flagged as overdue by '.$thisstaff->getName();
-                            foreach($_POST['tids'] as $k=>$v) {
-                                if(($t=Ticket::lookup($v)) && !$t->isOverdue() && $t->markOverdue()) {
-                                    $i++;
-                                    $t->logNote('Ticket Marked Overdue', $note, $thisstaff);
-                                }
-                            }
-
-                            if($i==$count)
-                                $msg = "Selected tickets ($i) marked overdue";
-                            elseif($i)
-                                $warn = "$i of $count selected tickets marked overdue";
-                            else
-                                $errors['err'] = 'Unable to flag selected tickets as overdue';
-                            break;
-                        case 'delete':
-                            if($thisstaff->canDeleteTickets()) {
-                                foreach($_POST['tids'] as $k=>$v) {
-                                    if(($t=Ticket::lookup($v)) && @$t->delete()) $i++;
-                                }
-
-                                //Log a warning
-                                if($i) {
-                                    $log = sprintf('%s (%s) just deleted %d ticket(s)',
-                                            $thisstaff->getName(), $thisstaff->getUserName(), $i);
-                                    $ost->logWarning('Tickets deleted', $log, false);
-
-                                }
-
-                                if($i==$count)
-                                    $msg = "Selected tickets ($i) deleted successfully";
-                                elseif($i)
-                                    $warn = "$i of $count selected tickets deleted";
-                                else
-                                    $errors['err'] = 'Unable to delete selected tickets';
-                            } else {
-                                $errors['err'] = 'You do not have permission to delete tickets';
-                            }
-                            break;
-                        default:
-                            $errors['err']='Unknown or unsupported action - get technical help';
-                    }
-                }
-                break;
             case 'open':
                 $ticket=null;
                 if(!$thisstaff || !$thisstaff->canCreateTickets()) {
-                     $errors['err']='You do not have permission to create tickets. Contact admin for such access';
+                     $errors['err'] = sprintf('%s %s',
+                             sprintf(__('You do not have permission %s.'),
+                                 __('to create tickets')),
+                             __('Contact admin for such access'));
                 } else {
                     $vars = $_POST;
                     $vars['uid'] = $user? $user->getId() : 0;
 
+                    $vars['cannedattachments'] = $response_form->getField('attachments')->getClean();
+
                     if(($ticket=Ticket::open($vars, $errors))) {
-                        $msg='Ticket created successfully';
+                        $msg=__('Ticket created successfully');
                         $_REQUEST['a']=null;
                         if (!$ticket->checkStaffAccess($thisstaff) || $ticket->isClosed())
                             $ticket=null;
                         Draft::deleteForNamespace('ticket.staff%', $thisstaff->getId());
+                        // Drop files from the response attachments widget
+                        $response_form->setSource(array());
+                        $response_form->getField('attachments')->reset();
                         unset($_SESSION[':form-data']);
                     } elseif(!$errors['err']) {
-                        $errors['err']='Unable to create the ticket. Correct the error(s) and try again';
+                        $errors['err']=__('Unable to create the ticket. Correct the error(s) and try again');
                     }
                 }
                 break;
@@ -515,25 +370,29 @@ $stats= $thisstaff->getTicketsStats();
 
 //Navigation
 $nav->setTabActive('tickets');
+$open_name = _P('queue-name',
+    /* This is the name of the open ticket queue */
+    'Open');
 if($cfg->showAnsweredTickets()) {
-    $nav->addSubMenu(array('desc'=>'Open ('.number_format($stats['open']+$stats['answered']).')',
-                            'title'=>'Open Tickets',
+    $nav->addSubMenu(array('desc'=>$open_name.' ('.number_format($stats['open']+$stats['answered']).')',
+                            'title'=>__('Open Tickets'),
                             'href'=>'tickets.php',
                             'iconclass'=>'Ticket'),
                         (!$_REQUEST['status'] || $_REQUEST['status']=='open'));
 } else {
 
-    if($stats) {
-        $nav->addSubMenu(array('desc'=>'Open ('.number_format($stats['open']).')',
-                               'title'=>'Open Tickets',
+    if ($stats) {
+
+        $nav->addSubMenu(array('desc'=>$open_name.' ('.number_format($stats['open']).')',
+                               'title'=>__('Open Tickets'),
                                'href'=>'tickets.php',
                                'iconclass'=>'Ticket'),
                             (!$_REQUEST['status'] || $_REQUEST['status']=='open'));
     }
 
     if($stats['answered']) {
-        $nav->addSubMenu(array('desc'=>'Answered ('.number_format($stats['answered']).')',
-                               'title'=>'Answered Tickets',
+        $nav->addSubMenu(array('desc'=>__('Answered').' ('.number_format($stats['answered']).')',
+                               'title'=>__('Answered Tickets'),
                                'href'=>'tickets.php?status=answered',
                                'iconclass'=>'answeredTickets'),
                             ($_REQUEST['status']=='answered'));
@@ -542,42 +401,42 @@ if($cfg->showAnsweredTickets()) {
 
 if($stats['assigned']) {
 
-    $nav->addSubMenu(array('desc'=>'My Tickets ('.number_format($stats['assigned']).')',
-                           'title'=>'Assigned Tickets',
+    $nav->addSubMenu(array('desc'=>__('My Tickets').' ('.number_format($stats['assigned']).')',
+                           'title'=>__('Assigned Tickets'),
                            'href'=>'tickets.php?status=assigned',
                            'iconclass'=>'assignedTickets'),
                         ($_REQUEST['status']=='assigned'));
 }
 
 if($stats['overdue']) {
-    $nav->addSubMenu(array('desc'=>'Overdue ('.number_format($stats['overdue']).')',
-                           'title'=>'Stale Tickets',
+    $nav->addSubMenu(array('desc'=>__('Overdue').' ('.number_format($stats['overdue']).')',
+                           'title'=>__('Stale Tickets'),
                            'href'=>'tickets.php?status=overdue',
                            'iconclass'=>'overdueTickets'),
                         ($_REQUEST['status']=='overdue'));
 
     if(!$sysnotice && $stats['overdue']>10)
-        $sysnotice=$stats['overdue'] .' overdue tickets!';
+        $sysnotice=sprintf(__('%d overdue tickets!'),$stats['overdue']);
 }
 
 if($thisstaff->showAssignedOnly() && $stats['closed']) {
-    $nav->addSubMenu(array('desc'=>'My Closed Tickets ('.number_format($stats['closed']).')',
-                           'title'=>'My Closed Tickets',
+    $nav->addSubMenu(array('desc'=>__('My Closed Tickets').' ('.number_format($stats['closed']).')',
+                           'title'=>__('My Closed Tickets'),
                            'href'=>'tickets.php?status=closed',
                            'iconclass'=>'closedTickets'),
                         ($_REQUEST['status']=='closed'));
 } else {
 
-    $nav->addSubMenu(array('desc'=>'Closed Tickets ('.number_format($stats['closed']).')',
-                           'title'=>'Closed Tickets',
+    $nav->addSubMenu(array('desc' => __('Closed').' ('.number_format($stats['closed']).')',
+                           'title'=>__('Closed Tickets'),
                            'href'=>'tickets.php?status=closed',
                            'iconclass'=>'closedTickets'),
                         ($_REQUEST['status']=='closed'));
 }
 
 if($thisstaff->canCreateTickets()) {
-    $nav->addSubMenu(array('desc'=>'New Ticket',
-                           'title' => 'Open New Ticket',
+    $nav->addSubMenu(array('desc'=>__('New Ticket'),
+                           'title'=> __('Open a New Ticket'),
                            'href'=>'tickets.php?a=open',
                            'iconclass'=>'newTicket',
                            'id' => 'new-ticket'),
@@ -591,7 +450,7 @@ $ost->addExtraHeader('<meta name="tip-namespace" content="tickets.queue" />',
 
 $inc = 'tickets.inc.php';
 if($ticket) {
-    $ost->setPageTitle('Ticket #'.$ticket->getNumber());
+    $ost->setPageTitle(sprintf(__('Ticket #%s'),$ticket->getNumber()));
     $nav->setActiveSubMenu(-1);
     $inc = 'ticket-view.inc.php';
     if($_REQUEST['a']=='edit' && $thisstaff->canEditTickets()) {
@@ -600,20 +459,19 @@ if($ticket) {
         // Auto add new fields to the entries
         foreach ($forms as $f) $f->addMissingFields();
     } elseif($_REQUEST['a'] == 'print' && !$ticket->pdfExport($_REQUEST['psize'], $_REQUEST['notes']))
-        $errors['err'] = 'Internal error: Unable to export the ticket to PDF for print.';
+        $errors['err'] = __('Internal error: Unable to export the ticket to PDF for print.');
 } else {
-    $inc = 'tickets.inc.php';
+	$inc = 'tickets.inc.php';
     if($_REQUEST['a']=='open' && $thisstaff->canCreateTickets())
         $inc = 'ticket-open.inc.php';
     elseif($_REQUEST['a'] == 'export') {
-        require_once(INCLUDE_DIR.'class.export.php');
         $ts = strftime('%Y%m%d');
         if (!($token=$_REQUEST['h']))
-            $errors['err'] = 'Query token required';
+            $errors['err'] = __('Query token required');
         elseif (!($query=$_SESSION['search_'.$token]))
-            $errors['err'] = 'Query token not found';
+            $errors['err'] = __('Query token not found');
         elseif (!Export::saveTickets($query, "tickets-$ts.csv", 'csv'))
-            $errors['err'] = 'Internal error: Unable to dump query results';
+            $errors['err'] = __('Internal error: Unable to dump query results');
     }
 
     //Clear active submenu on search with no status
@@ -632,4 +490,5 @@ if($ticket) {
 
 require_once(STAFFINC_DIR.'header.inc.php');
 require_once(STAFFINC_DIR.$inc);
+print $response_form->getMedia();
 require_once(STAFFINC_DIR.'footer.inc.php');
diff --git a/scp/upgrade.php b/scp/upgrade.php
index 5664c63f5167c231da937f962a14f759ec374deb..6ee00329120083d1932b0d5a3cd3197f4b36c517 100644
--- a/scp/upgrade.php
+++ b/scp/upgrade.php
@@ -23,13 +23,13 @@ if($_POST && $_POST['s'] && !$upgrader->isAborted()) {
     switch(strtolower($_POST['s'])) {
         case 'prereq':
             if(!$ost->isUpgradePending()) {
-                $errors['err']=' Nothing to do! System already upgraded to the current version';
+                $errors['err']=__('Nothing to do! System already upgraded to the current version');
             } elseif(!$upgrader->isUpgradable()) {
-                $errors['err']='The upgrader does NOT support upgrading from the current vesion!';
+                $errors['err']=__('The upgrader does NOT support upgrading from the current vesion!');
             } elseif(!$upgrader->check_prereq()) {
-                $errors['prereq']='Minimum requirements not met! Refer to Release Notes for more information';
+                $errors['prereq']=__('Minimum requirements not met! Refer to Release Notes for more information');
             } elseif(!strcasecmp(basename(CONFIG_FILE), 'settings.php')) {
-                $errors['err']='Config file rename required to continue!';
+                $errors['err']=__('Config file rename required to continue!');
             } else {
                 $upgrader->setState('upgrade');
             }
@@ -48,7 +48,7 @@ if($_POST && $_POST['s'] && !$upgrader->isAborted()) {
             }
             break;
         default:
-            $errors['err']='Unknown action!';
+            $errors['err']=__('Unknown action');
     }
 }
 
@@ -69,16 +69,16 @@ switch(strtolower($upgrader->getState())) {
         elseif(!strcasecmp(basename(CONFIG_FILE), 'settings.php'))
             $inc='rename.inc.php';
         elseif(!$ost->isUpgradePending())
-            $errors['err']='Nothing to do! System already upgraded to <b>'.$ost->getVersion().'</b> with no pending patches to apply.';
+            $errors['err']=sprintf(__('Nothing to do! System already upgraded to <b>%s</b> with no pending patches to apply.'),$ost->getVersion());
         elseif(!$upgrader->isUpgradable())
-            $errors['err']=sprintf('The upgrader does NOT support upgrading from the current patch [%s]!', $cfg->getSchemaSignature());
+            $errors['err']=sprintf(__('The upgrader does NOT support upgrading from the current patch [%s]!'), $cfg->getSchemaSignature());
 
 }
 
 $nav = new AdminNav($thisstaff);
 $nav->setTabActive('dashboard');
-$nav->addSubMenu(array('desc'=>'Upgrader',
-                           'title'=>'Upgrader',
+$nav->addSubMenu(array('desc'=>__('Upgrader'),
+                           'title'=>__('Upgrader'),
                            'href'=>'upgrade.php',
                            'iconclass'=>'preferences'),
                         true);
diff --git a/scp/users.php b/scp/users.php
index c6606a8be9737de524f8a3cb6a72eafe184bd2df..ade2474137ff87dd793e9ebc01d9aad9dcd2a98c 100644
--- a/scp/users.php
+++ b/scp/users.php
@@ -18,53 +18,57 @@ require_once INCLUDE_DIR.'class.note.php';
 
 $user = null;
 if ($_REQUEST['id'] && !($user=User::lookup($_REQUEST['id'])))
-    $errors['err'] = 'Unknown or invalid user ID.';
+    $errors['err'] = sprintf(__('%s: Unknown or invalid'), _N('end user', 'end users', 1));
 
 if ($_POST) {
     switch(strtolower($_REQUEST['do'])) {
         case 'update':
             if (!$user) {
-                $errors['err']='Unknown or invalid user.';
+                $errors['err']=sprintf(__('%s: Unknown or invalid'), _N('end user', 'end users', 1));
             } elseif(($acct = $user->getAccount())
                     && !$acct->update($_POST, $errors)) {
-                 $errors['err']='Unable to update user account information';
+                 $errors['err']=__('Unable to update user account information');
             } elseif($user->updateInfo($_POST, $errors)) {
-                $msg='User updated successfully';
+                $msg=sprintf(__('Successfully updated %s'), __('this end user'));
                 $_REQUEST['a'] = null;
             } elseif(!$errors['err']) {
-                $errors['err']='Unable to update user profile. Correct any error(s) below and try again!';
+                $errors['err']=sprintf(__('Unable to update %s. Correct error(s) below and try again!'),
+                    __('this end user'));
             }
             break;
         case 'create':
             $form = UserForm::getUserForm()->getForm($_POST);
             if (($user = User::fromForm($form))) {
-                $msg = Format::htmlchars($user->getName()).' added successfully';
+                $msg = Format::htmlchars(sprintf(__('Successfully added %s'), $user->getName()));
                 $_REQUEST['a'] = null;
             } elseif (!$errors['err']) {
-                $errors['err'] = 'Unable to add user. Correct any error(s) below and try again.';
+                $errors['err'] = sprintf(__('Unable to add %s. Correct error(s) below and try again.'),
+                    __('this end user'));
             }
             break;
         case 'confirmlink':
             if (!$user || !$user->getAccount())
-                $errors['err'] = 'Unknown or invalid user account';
+                $errors['err'] = sprintf(__('%s: Unknown or invalid'),
+                    __('end user account'));
             elseif ($user->getAccount()->isConfirmed())
-                $errors['err'] = 'Account is already confirmed';
+                $errors['err'] = __('Account is already confirmed');
             elseif ($user->getAccount()->sendConfirmEmail())
-                $msg = 'Account activation email sent to '.$user->getEmail();
+                $msg = sprintf(__('Account activation email sent to %s'),$user->getEmail());
             else
-                $errors['err'] = 'Unable to send account activation email - try again!';
+                $errors['err'] = __('Unable to send account activation email - try again!');
             break;
         case 'pwreset':
             if (!$user || !$user->getAccount())
-                $errors['err'] = 'Unknown or invalid user account';
+                $errors['err'] = sprintf(__('%s: Unknown or invalid'), __('end user account'));
             elseif ($user->getAccount()->sendResetEmail())
-                $msg = 'Account password reset email sent to '.$user->getEmail();
+                $msg = sprintf(__('Account password reset email sent to %s'),$user->getEmail());
             else
-                $errors['err'] = 'Unable to send account password reset email - try again!';
+                $errors['err'] = __('Unable to send account password reset email - try again!');
             break;
         case 'mass_process':
             if (!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) {
-                $errors['err'] = 'You must select at least one user member.';
+                $errors['err'] = sprintf(__('You must select at least %s.'),
+                    __('one end user'));
             } else {
                 $errors['err'] = "Coming soon!";
             }
@@ -72,23 +76,24 @@ if ($_POST) {
         case 'import-users':
             $status = User::importFromPost($_FILES['import'] ?: $_POST['pasted']);
             if (is_numeric($status))
-                $msg = "Successfully imported $status clients";
+                $msg = sprintf(__('Successfully imported %1$d %2$s.'), $status,
+                    _N('end user', 'end users', $status));
             else
                 $errors['err'] = $status;
             break;
         default:
-            $errors['err'] = 'Unknown action/command';
+            $errors['err'] = __('Unknown action');
             break;
     }
 } elseif($_REQUEST['a'] == 'export') {
     require_once(INCLUDE_DIR.'class.export.php');
     $ts = strftime('%Y%m%d');
     if (!($token=$_REQUEST['qh']))
-        $errors['err'] = 'Query token required';
+        $errors['err'] = __('Query token required');
     elseif (!($query=$_SESSION['users_qs_'.$token]))
-        $errors['err'] = 'Query token not found';
-    elseif (!Export::saveUsers($query, "users-$ts.csv", 'csv'))
-        $errors['err'] = 'Internal error: Unable to dump query results';
+        $errors['err'] = __('Query token not found');
+    elseif (!Export::saveUsers($query, __("users")."-$ts.csv", 'csv'))
+        $errors['err'] = __('Internal error: Unable to dump query results');
 }
 
 $page = $user? 'user-view.inc.php' : 'users.inc.php';
diff --git a/setup/ajax.php b/setup/ajax.php
index 97e45daddc88ece9cd65e24ea71dfb9f0d7ac8b9..ad1e18432b705b4c7377f7557d6559cc2a2ba448 100644
--- a/setup/ajax.php
+++ b/setup/ajax.php
@@ -17,7 +17,7 @@
 require('setup.inc.php');
 
 if(!defined('INCLUDE_DIR'))
-    Http::response(500, 'Server configuration error');
+    Http::response(500, __('Server configuration error'));
 require_once INCLUDE_DIR.'/class.dispatcher.php';
 require_once INCLUDE_DIR.'/class.ajax.php';
 
diff --git a/setup/cli/modules/i18n.php b/setup/cli/modules/i18n.php
index a9a4117c4b15f59d6914dbd11edf741b1c5544b7..57a8bd0abf91f0d5fc6d016c22c56928bfeb9199 100644
--- a/setup/cli/modules/i18n.php
+++ b/setup/cli/modules/i18n.php
@@ -9,8 +9,15 @@ class i18n_Compiler extends Module {
     var $prologue = "Manages translation files from Crowdin";
 
     var $arguments = array(
-        "command" => "Action to be performed.
-            list    - Show list of available translations"
+        "command" => array(
+            'help' => "Action to be performed.",
+            "options" => array(
+                'list' =>       'Show list of available translations',
+                'build' =>      'Compile a language pack',
+                'make-pot' =>   'Build the PO file for gettext translations',
+                'sign' =>       'Sign a language pack',
+            ),
+        ),
     );
 
     var $options = array(
@@ -19,11 +26,22 @@ class i18n_Compiler extends Module {
             CROWDIN_API_KEY is defined in the ost-config.php file'),
         "lang" => array('-L', '--lang', 'metavar'=>'code',
             'help'=>'Language code (used for building)'),
+        'file' => array('-f', '--file', 'metavar'=>'FILE',
+            'help' => "Language pack to be signed"),
+        'pkey' => array('-P', '--pkey', 'metavar'=>'key-file',
+            'help' => 'Private key for signing'),
+        'root' => array('-R', '--root', 'matavar'=>'path',
+            'help' => 'Specify a root folder for `make-pot`'),
+        'domain' => array('-D', '--domain', 'metavar'=>'name',
+            'default' => '',
+            'help' => 'Add a domain to the path/context of PO strings'),
     );
 
-    static $crowdin_api_url = 'http://i18n.osticket.com/api/project/osticket-official/{command}';
+    static $project = 'osticket-official';
+    static $crowdin_api_url = 'http://i18n.osticket.com/api/project/{project}/{command}';
 
     function _http_get($url) {
+        $this->stdout->write(">>> Downloading $url\n");
         #curl post
         $ch = curl_init();
         curl_setopt($ch, CURLOPT_URL, $url);
@@ -40,7 +58,9 @@ class i18n_Compiler extends Module {
 
     function _request($command, $args=array()) {
 
-        $url = str_replace('{command}', $command, self::$crowdin_api_url);
+        $url = str_replace(array('{command}', '{project}'),
+            array($command, self::$project),
+            self::$crowdin_api_url);
 
         $args += array('key' => $this->key);
         foreach ($args as &$a)
@@ -56,6 +76,9 @@ class i18n_Compiler extends Module {
         if (!$this->key && defined('CROWDIN_API_KEY'))
             $this->key = CROWDIN_API_KEY;
 
+        function get_osticket_root_path() { return ROOT_DIR; }
+        require_once(ROOT_DIR.'setup/test/tests/class.test.php');
+
         switch (strtolower($args['command'])) {
         case 'list':
             if (!$this->key)
@@ -69,6 +92,14 @@ class i18n_Compiler extends Module {
                 $this->fail('Language code is required. See `list`');
             $this->_build($options['lang']);
             break;
+        case 'make-pot':
+            $this->_make_pot($options);
+            break;
+        case 'sign':
+            if (!$options['file'] || !file_exists($options['file']))
+                $this->fail('Specify a language pack to sign with --file=');
+            $this->_sign($options['file'], $options);
+            break;
         }
     }
 
@@ -114,15 +145,476 @@ class i18n_Compiler extends Module {
         $lang = str_replace('-','_',$lang);
         @unlink(I18N_DIR."$lang.phar");
         $phar = new Phar(I18N_DIR."$lang.phar");
+        $phar->startBuffering();
+
+        $po_file = false;
 
         for ($i=0; $i<$zip->numFiles; $i++) {
             $info = $zip->statIndex($i);
-            $phar->addFromString($info['name'], $zip->getFromIndex($i));
+            $contents = $zip->getFromIndex($i);
+            if (!$contents)
+                continue;
+            if (strpos($info['name'], '/messages.po') !== false) {
+                $po_file = $contents;
+                // Don't add the PO file as-is to the PHAR file
+                continue;
+            }
+            $phar->addFromString($info['name'], $contents);
         }
 
         // TODO: Add i18n extras (like fonts)
+        // Redactor language pack
+        //
+        $langs = array($lang);
+        if (strpos($lang, '_') !== false) {
+            @list($short) = explode('_', $lang);
+            $langs[] = $short;
+        }
+        foreach ($langs as $l) {
+            list($code, $js) = $this->_http_get(
+                'http://imperavi.com/webdownload/redactor/lang/?lang='
+                .strtolower($l));
+            if ($code == 200 && ($js != 'File not found')) {
+                $phar->addFromString('js/redactor.js', $js);
+                break;
+            }
+        }
+        if ($code != 200)
+            $this->stderr->write($lang . ": Unable to fetch Redactor language file\n");
+
+        // JQuery UI Datepicker
+        // http://jquery-ui.googlecode.com/svn/tags/latest/ui/i18n/jquery.ui.datepicker-de.js
+        foreach ($langs as $l) {
+            list($code, $js) = $this->_http_get(
+                'http://jquery-ui.googlecode.com/svn/tags/latest/ui/i18n/jquery.ui.datepicker-'
+                    .str_replace('_','-',$l).'.js');
+            // If locale-specific version is not available, use the base
+            // language version (de if de_CH is not available)
+            if ($code == 200)
+                break;
+        }
+        if ($code == 200)
+            $phar->addFromString('js/jquery.ui.datepicker.js', $js);
+        else
+            $this->stderr->write(str_replace('_','-',$lang)
+                .": Unable to fetch jQuery UI Datepicker locale file\n");
+
+        // True type fonts for PDF printing
+        $langs = (include I18N_DIR . 'langs.php');
+        $info = $langs[$lang];
+        if (isset($info['fonts'])) {
+            foreach ($info['fonts'] as $name => $types) {
+                foreach ($types as $code => $fullname) {
+                    list($ttf, $url) = $fullname;
+                    if (!$url)
+                        continue;
+                    list($code, $file) = $this->_http_get($url);
+                    if ($code == 200)
+                        $phar->addFromString('fonts/'.$ttf, $file);
+                    else
+                        $this->stderr->write(
+                            "*** Unable to fetch $url\n");
+                }
+            }
+        }
+
+        // Add in the messages.mo.php file
+        if ($po_file) {
+            $pipes = array();
+            $msgfmt = proc_open('msgfmt -o- -',
+                array(0=>array('pipe','r'), 1=>array('pipe','w')),
+                $pipes);
+            if (is_resource($msgfmt)) {
+                fwrite($pipes[0], $po_file);
+                fclose($pipes[0]);
+                $mo_input = fopen('php://temp', 'r+b');
+                fwrite($mo_input, stream_get_contents($pipes[1]));
+                rewind($mo_input);
+                require_once INCLUDE_DIR . 'class.translation.php';
+                $mo = Translation::buildHashFile($mo_input, false, true);
+                $phar->addFromString('LC_MESSAGES/messages.mo.php', $mo);
+                fclose($mo_input);
+            }
+        }
+
+        // Add in translation of javascript strings
+        $phrases = array();
+        if ($mo && ($js = $this->__getAllJsPhrases())) {
+            $mo = (eval (substr($mo, 5))); # Chop off <?php
+            foreach ($js as $c) {
+                foreach ($c['forms'] as $f) {
+                    $phrases[$f] = @$mo[$f] ?: $f;
+                }
+            }
+            $phar->addFromString(
+                'js/osticket-strings.js',
+                sprintf('(function($){$.oststrings=%s;})(jQuery);',
+                    JsonDataEncoder::encode($phrases))
+            );
+        }
+
+        list($code, $zip) = $this->_request("download/$lang.zip");
+
+        // Include a manifest
+        include_once INCLUDE_DIR . 'class.mailfetch.php';
+
+        $po_header = Mail_Parse::splitHeaders($mo['']);
+        $info = array(
+            'Build-Date' => date(DATE_RFC822),
+            'Build-Version' => trim(`git describe`),
+            'Language' => $po_header['Language'],
+            #'Phrases' =>
+            #'Translated' =>
+            #'Approved' =>
+            'Id' => 'lang:' . $lang,
+            'Last-Revision' => $po_header['PO-Revision-Date'],
+            'Version' => (int)(strtotime($po_header['PO-Revision-Date']) / 10000),
+        );
+        $phar->addFromString(
+            'MANIFEST.php',
+            sprintf('<?php return %s;', var_export($info, true)));
 
         // TODO: Sign files
+
+        // Use a very small stub
+        $phar->setStub('<?php __HALT_COMPILER();');
+        $phar->setSignatureAlgorithm(Phar::SHA1);
+        $phar->stopBuffering();
+    }
+
+    function _sign($plugin, $options) {
+        if (!file_exists($plugin))
+            $this->fail($plugin.': Cannot find file');
+        elseif (!file_exists("phar://$plugin/MANIFEST.php"))
+            $this->fail($plugin.': Should be a plugin PHAR file');
+        $info = (include "phar://$plugin/MANIFEST.php");
+        $phar = new Phar($plugin);
+
+        if (!function_exists('openssl_get_privatekey'))
+            $this->fail('OpenSSL extension required for signing');
+        $private = openssl_get_privatekey(
+                file_get_contents($options['pkey']));
+        if (!$private)
+            $this->fail('Unable to read private key');
+        $signature = $phar->getSignature();
+        $seal = '';
+        openssl_sign($signature['hash'], $seal, $private,
+            OPENSSL_ALGO_SHA1);
+        if (!$seal)
+            $this->fail('Unable to generate verify signature');
+
+        $this->stdout->write(sprintf("Signature: %s\n",
+            strtolower($signature['hash'])));
+        $this->stdout->write(
+            sprintf("Seal: \"v=1; i=%s; s=%s; V=%s;\"\n",
+            $info['Id'], base64_encode($seal), $info['Version']));
+    }
+
+    function __read_next_string($tokens) {
+        $string = array();
+
+        while (list(,$T) = each($tokens)) {
+            switch ($T[0]) {
+                case T_CONSTANT_ENCAPSED_STRING:
+                    // Strip leading and trailing ' and " chars
+                    $string['form'] = preg_replace(array("`^{$T[1][0]}`","`{$T[1][0]}$`"),array("",""), $T[1]);
+                    $string['line'] = $T[2];
+                    break;
+                case T_DOC_COMMENT:
+                case T_COMMENT:
+                    switch ($T[1][0]) {
+                    case '/':
+                        if ($T[1][1] == '*')
+                            $text = trim($T[1], '/* ');
+                        else
+                            $text = ltrim($T[1], '/ ');
+                        break;
+                    case '#':
+                        $text = ltrim($T[1], '# ');
+                    }
+                    $string['comments'][] = $text;
+                    break;
+                case T_WHITESPACE:
+                    // noop
+                    continue;
+                case T_STRING_VARNAME:
+                case T_NUM_STRING:
+                case T_ENCAPSED_AND_WHITESPACE:
+                case '.':
+                    $string['constant'] = false;
+                    break;
+                case '[':
+                    // Not intended to be translated — array index
+                    return null;
+                default:
+                    return array($string, $T);
+            }
+        }
+    }
+    function __read_args($tokens, $proto=false) {
+        $args = array('forms'=>array());
+        $arg = null;
+        $proto = $proto ?: array('forms'=>1);
+
+        while (list($string,$T) = $this->__read_next_string($tokens)) {
+            // Add context and forms
+            if (isset($proto['context']) && !isset($args['context'])) {
+                $args['context'] = $string['form'];
+            }
+            elseif (count($args['forms']) < $proto['forms'] && $string) {
+                if (isset($string['constant']) && !$string['constant']) {
+                    throw new Exception($string['form'] . ': Untranslatable string');
+                }
+                $args['forms'][] = $string['form'];
+            }
+            elseif ($string) {
+                $this->stderr->write(sprintf("%s: %s: Too many arguments\n",
+                    $string['line'] ?: '?', $string['form']));
+            }
+
+            // Add usage and comment info
+            if (!isset($args['line']) && isset($string['line']))
+                $args['line'] = $string['line'];
+            if (isset($string['comments']))
+                $args['comments'] = array_merge(
+                    @$args['comments'] ?: array(), $string['comments']);
+
+            // Handle the terminating token from ::__read_next_string()
+            switch ($T[0]) {
+            case ')':
+                return $args;
+            }
+        }
+    }
+
+    function __get_func_args($tokens, $args) {
+        while (list(,$T) = each($tokens)) {
+            switch ($T[0]) {
+            case T_WHITESPACE:
+                continue;
+            case '(':
+                return $this->__read_args($tokens, $args);
+            default:
+                // Not a function call
+                return false;
+            }
+        }
+    }
+    function __find_strings($tokens, $funcs, $parens=0) {
+        $T_funcs = array();
+        $funcdef = false;
+        while (list(,$T) = each($tokens)) {
+            switch ($T[0]) {
+            case T_STRING:
+            case T_VARIABLE:
+                if ($funcdef)
+                    break;
+                if ($T[1] == 'sprintf') {
+                    foreach ($this->__find_strings($tokens, $funcs) as $i=>$f) {
+                        // Only the first on gets the php-format flag
+                        if ($i == 0)
+                            $f['flags'] = array('php-format');
+                        $T_funcs[] = $f;
+                    }
+                    break;
+                }
+                if (!isset($funcs[$T[1]]))
+                    continue;
+                $constants = $funcs[$T[1]];
+                if ($info = $this->__get_func_args($tokens, $constants))
+                    $T_funcs[] = $info;
+                break;
+            case T_COMMENT:
+            case T_DOC_COMMENT:
+                $translate = false;
+                $hints = array();
+                if (preg_match('`^/\*+\s*@(\w+)`m', $T[1])) {
+                    foreach (preg_split('`,\s*`m', $T[1]) as $command) {
+                        $command = trim($command, " \n\r\t\"*/\\");
+                        @list($command, $args) = explode(' ', $command, 2);
+                        switch ($command) {
+                        case '@context':
+                            $hints['context'] = trim($args, " \"*\n\t\r");
+                        case '@trans':
+                            $translate = true;
+                        default:
+                            continue;
+                        }
+                    }
+                }
+                if ($translate) {
+                    // Find the next textual token
+                    list($S, $T) = $this->__read_next_string($tokens);
+                    $string = array('forms'=>array($S['form']), 'line'=>$S['line'])
+                        + $hints;
+                    if (isset($S['comments']))
+                        $string['comments'] = $S['comments'];
+                    $T_funcs[] = $string;
+                }
+                break;
+            // Track function definitions of the gettext functions
+            case T_FUNCTION:
+                $funcdef = true;
+                break;
+            case '{';
+                $funcdef = false;
+            case '(':
+                $parens++;
+                break;
+            case ')':
+                // End of scope?
+                if (--$parens == 0)
+                    return $T_funcs;
+            }
+        }
+        return $T_funcs;
+    }
+
+    function __write_string($string) {
+        // Unescape single quote (') and escape unescaped double quotes (")
+        $string = preg_replace(array("`\\\(['$])`", '`(?<!\\\)"`'), array("$1", '\"'), $string);
+        // Preserve embedded newlines -- preserve up to on
+        $string = preg_replace("`\n`u", "\\n\n", $string);
+        // Word-wrap long lines
+        $string = rtrim(preg_replace('/(?=[\s\p{Ps}])(.{1,76})(\s|$|(\p{Ps}))/uS',
+            "$1$2\n", $string), "\n");
+        $strings = array_filter(explode("\n", $string));
+
+        if (count($strings) > 1)
+            array_unshift($strings, "");
+        foreach ($strings as $line) {
+            print "\"{$line}\"\n";
+        }
+    }
+    function __write_pot_header() {
+        $lines = array(
+            'msgid ""',
+            'msgstr ""',
+            '"Project-Id-Version: osTicket '.trim(`git describe`).'\n"',
+            '"POT-Create-Date: '.date('Y-m-d H:i O').'\n"',
+            '"Report-Msgid-Bugs-To: support@osticket.com\n"',
+            '"Language: en_US\n"',
+            '"MIME-Version: 1.0\n"',
+            '"Content-Type: text/plain; charset=UTF-8\n"',
+            '"Content-Transfer-Encoding: 8bit\n"',
+            '"X-Generator: osTicket i18n CLI\n"',
+        );
+        print implode("\n", $lines);
+        print "\n";
+    }
+    function __write_pot($strings) {
+        $this->__write_pot_header();
+        foreach ($strings as $S) {
+            print "\n";
+            if ($c = @$S['comments']) {
+                foreach ($c as $comment) {
+                    foreach (explode("\n", $comment) as $line) {
+                        if ($line = trim($line))
+                            print "#. {$line}\n";
+                    }
+                }
+            }
+            foreach ($S['usage'] as $ref) {
+                print "#: ".$ref."\n";
+            }
+            if ($f = @$S['flags']) {
+                print "#, ".implode(', ', $f)."\n";
+            }
+            if (isset($S['context'])) {
+                print "msgctxt ";
+                $this->__write_string($S['context']);
+            }
+            print "msgid ";
+            $this->__write_string($S['forms'][0]);
+            if (count($S['forms']) == 2) {
+                print "msgid_plural ";
+                $this->__write_string($S['forms'][1]);
+                print 'msgstr[0] ""'."\n";
+                print 'msgstr[1] ""'."\n";
+            }
+            else {
+                print 'msgstr ""'."\n";
+            }
+        }
+    }
+
+    function _make_pot($options) {
+        error_reporting(E_ALL);
+        $funcs = array(
+            '__'    => array('forms'=>1),
+            '$__'   => array('forms'=>1),
+            '_S'    => array('forms'=>1),
+            '_N'    => array('forms'=>2),
+            '$_N'   => array('forms'=>2),
+            '_NS'   => array('forms'=>2),
+            '_P'    => array('context'=>1, 'forms'=>1),
+            '_NP'   => array('context'=>1, 'forms'=>2),
+            // This is an error
+            '_'     => array('forms'=>0),
+        );
+        $root = realpath($options['root'] ?: ROOT_DIR);
+        $domain = $options['domain'] ? '('.$options['domain'].')/' : '';
+        $files = Test::getAllScripts(true, $root);
+        $strings = array();
+        foreach ($files as $f) {
+            $F = str_replace($root.'/', $domain, $f);
+            $this->stderr->write("$F\n");
+            $tokens = new ArrayObject(token_get_all(fread(fopen($f, 'r'), filesize($f))));
+            foreach ($this->__find_strings($tokens, $funcs, 1) as $call) {
+                self::__addString($strings, $call, $F);
+            }
+        }
+        $strings = array_merge($strings, $this->__getAllJsPhrases($root));
+        $this->__write_pot($strings);
+    }
+
+    static function __addString(&$strings, $call, $file=false) {
+        if (!($forms = @$call['forms']))
+            // Transation of non-constant
+            return;
+        $primary = $forms[0];
+        // Normalize the $primary string
+        $primary = preg_replace(array("`\\\(['$])`", '`(?<!\\\)"`'), array("$1", '\"'), $primary);
+        if (isset($call['context']))
+            $primary = $call['context'] . "\x04" . $primary;
+        if (!isset($strings[$primary])) {
+            $strings[$primary] = array('forms' => $forms);
+        }
+        $E = &$strings[$primary];
+
+        if (isset($call['line']) && $file)
+            $E['usage'][] = "{$file}:{$call['line']}";
+        if (isset($call['flags']))
+            $E['flags'] = array_unique(array_merge(@$E['flags'] ?: array(), $call['flags']));
+        if (isset($call['comments']))
+            $E['comments'] = array_merge(@$E['comments'] ?: array(), $call['comments']);
+        if (isset($call['context']))
+            $E['context'] = $call['context'];
+    }
+
+    function __getAllJsPhrases($root=ROOT_DIR) {
+        $strings = array();
+        $root = rtrim($root, '/') . '/';
+        $funcs = array('__'=>array('forms'=>1));
+        foreach (glob_recursive($root . "*.js") as $s) {
+            $script = file_get_contents($s);
+            $s = str_replace($root, '', $s);
+            $this->stderr->write($s."\n");
+            $calls = array();
+            preg_match_all('/(?:function\s+)?__\(\s*[^\'"]*(([\'"])(?:(?<!\\\\)\2|.)+\2)\s*[^)]*\)/',
+                $script, $calls, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
+            foreach ($calls as $c) {
+                if (!($call = $this->__find_strings(token_get_all('<?php '.$c[0][0]), $funcs, 0)))
+                    continue;
+                $call = $call[0];
+
+                list($lhs) = str_split($script, $c[1][1]);
+                $call['line'] = strlen($lhs) - strlen(str_replace("\n", "", $lhs)) + 1;
+
+                self::__addString($strings, $call, $s);
+            }
+        }
+        return $strings;
     }
 }
 
diff --git a/setup/css/wizard.css b/setup/css/wizard.css
index 2dd02eced36596ac0fb570c21a6205e58de00023..838f2de0f8c3f87a153549c54dbcb4c2cc1ea93b 100644
--- a/setup/css/wizard.css
+++ b/setup/css/wizard.css
@@ -1,5 +1,5 @@
 @import url("../../css/font-awesome.min.css");
-body { background: url('../images/background.jpg?1312906017') top left repeat-x white; font-family: arial, helvetica, sans-serif; font-size: 10pt; color: #000; margin: 0; padding: 0; }
+body { background: url('../images/background.jpg?1312906017') top left repeat-x white; font-family: arial, helvetica, sans-serif; font-size: 10pt; color: #000; margin: 0; padding: 0; line-height: 1.3em; }
 
 #wizard { background: #fff; width: 800px; margin: 30px auto; padding: 10px; border: 1px solid #2a67ac; border-right: 2px solid #2a67ac; border-bottom: 3px solid #2a67ac; overflow: hidden; margin-bottom:5px;}
 
@@ -11,16 +11,16 @@ a { color: #2a67ac; }
 .hidden { display: none;}
 .error { color:#f00;}
 
-#header { height: 72px; margin-bottom: 20px; }
+#header { height: 72px; margin-bottom: 20px; width: 100%; }
 #header #logo { width: 280px; height: 72px; display: block; float: left; }
-#header ul { margin: 0; padding: 0; width: 400px; float: right; text-align: right; }
-#header ul .info { font-size: 11pt; font-weight: bold; border-bottom: 1px solid #2a67ac; color: #444; }
+#header .info { font-size: 11pt; font-weight: bold; border-bottom: 1px solid #2a67ac; color: #444; text-align: right; float: right; }
+#header ul { margin: 0; padding: 0; text-align: right; }
 #header ul li { list-style: none; margin: 5px 0 0 0; padding: 0; }
-#header ul li a { color: #F3811C; text-decoration: none; }
+#header ul li a { color: #F3811C; text-decoration: none; white-space: nowrap; }
 #header ul li a:hover { color: #2a67ac; }
 
 #sidebar { width: 180px; padding: 10px; border: 1px solid #C8DDFA; float: right; background: #F7FBFE; }
-#sidebar h3 { font-size: 10pt; margin: 0 0 5px 0; padding: 0; text-indent: 32px; background: url('../images/cog.png?1312913866') top left no-repeat; line-height: 24px; color: #2a67ac; }
+#sidebar h3 { font-size: 10pt; margin: 0 0 5px 0; padding: 0; padding-left: 32px; background: url('../images/cog.png?1312913866') top left no-repeat; line-height: 1.2em; color: #2a67ac; min-height: 24px; }
 
 #footer { color:#ccc; }
 #footer a, #footer a:hover { text-decoration:none; color:#ccc; }
@@ -42,6 +42,10 @@ ul.progress li.no { background-image: url('../images/no.png?1312906277'); }
 ul.progress li.yes small {color:green; }
 ul.progress li.no small {color:red;}
 
+#intro ul li + li {
+    margin-top: 8px;
+}
+
 #bar { clear: both; padding-top: 10px; height: 24px; line-height: 24px; text-align: center; border-top: 1px solid #aaaaaa; }
 #bar a, #bar .btn { display: inline-block; margin: 0; height: 24px; line-height: 24px; font-weight: bold; border: 1px solid #666666; text-decoration: none; padding: 0 10px; background: url('../images/grey_btn_bg.png?1312910883') top left repeat-x; color: #333; }
 #bar a:hover, #bar .btn:hover, #bar .btnover { background-position: bottom left; }
@@ -56,9 +60,9 @@ form h4.system { background-image: url('../images/system.png?1262713696'); }
 form h4.admin { background-image: url('../images/user.png?1262713720'); }
 form h4.database { background-image: url('../images/database.png?1262713698'); }
 form h4.email { background-image: url('../images/email.png?1142218352'); }
-form .row { clear: both; padding: 2px 0 0 24px; background: #F7FBFE; height: 24px; }
-form .row input { background: #fff; height: 22px; padding: 0; line-height: 22px; border: 1px solid #cccccc; }
-form .row label, form .row span { display: block; float: left; width: 150px; line-height: 24px; }
+form .row { clear: both; padding: 2px 0 0 24px; background: #F7FBFE; }
+form .row input { background: #fff; height: 22px; padding: 0 5px; line-height: 22px; border: 1px solid #cccccc; }
+form .row label, form .row span { display: block; line-height: 24px; padding: 0 5px; }
 form .row span { width: 600px; color: #666666; }
 
 .tip_box {
@@ -109,7 +113,7 @@ form .row span { width: 600px; color: #666666; }
     position:absolute;
     top:0;
     left:-1px;
-    min-width:300px;
+    min-width:400px;
     line-height: 1.15rem;
 }
 
@@ -178,6 +182,23 @@ i.help-tip:hover {
 #loading { padding: 10px 10px 10px 60px; width: 400px; height: 150px; background: url('../images/ajax-loader.gif?1312925608') 10px 50% no-repeat white; position: fixed; display: none; z-index: 3000; }
 #loading h4 { margin: 3px 0 0 0; padding: 0; color: #d80; }
 
-.tip { display: inline-block; width: 16px; height: 16px; outline: none; text-decoration: none; color: #d80; }
+div.flags { text-align: right; margin-top: 5px; }
+a.flag { text-decoration: none; }
+
+.tip { display: inline-block; width: 16px; height: 16px; outline: none; text-decoration: none; color: #d80; margin-left: 5px; }
+.rtl .tip { margin-right: 5px;}
 
 #links { margin: 10px 0; }
+
+.rtl ul.progress li.yes, .rtl ul.progress li.no { background-position: 100%; }
+.rtl ul.progress li { padding-left: 0; padding-right: 24px; }
+.rtl form h4.head { padding-right: 24px; background-position: right center; background-position: calc(100% - 5px); }
+.rtl form .subhead { padding-right: 24px; }
+.rtl #header #logo { float: right; }
+.rtl #header .info, .rtl #header ul, .rtl div.flags { text-align:left; }
+.rtl #header .info { float: left; }
+.rtl .ltr { text-align: right; }
+.ltr {
+    direction: ltr;
+    unicode-bidi: embed;
+}
diff --git a/setup/doc/i18n.md b/setup/doc/i18n.md
new file mode 100644
index 0000000000000000000000000000000000000000..92409a0ded3d22ccd9c03050bb2d72e71c373a53
--- /dev/null
+++ b/setup/doc/i18n.md
@@ -0,0 +1,128 @@
+Internationalization
+====================
+
+System Languages
+----------------
+At various parts of the system, there are several possibilities for the
+language selection of the "local" language:
+
+  1. User (client or agent) preference
+  2. Ticket thread (for system activity notes)
+  3. System (for logs and administrative messages)
+
+The system is flexible enough to support these different cases and provides
+a few wrapper functions to connect your string to the appropriate language.
+Bear in mind when writing new code that strings may need to be translated
+into more than one language. For instance, if a string is to be displayed as
+an error in a web page as well as appear in an email to the administrator,
+it may need to be translated differently for both.
+
+Consider a Spanish-speaking user visiting a German-based help desk. After
+attempting to log in several times, they receive an error banner with a
+particular message. A message is also sent to the site administrator warning
+about possible brute force attack. These messages will need to consider
+different audiences when being localized. The site administrator should
+receive a warning email in German; whereas the user should see a Spanish
+error message with details about what to do next.
+
+Creating localized strings
+--------------------------
+Creating localized strings in osTicket is similar to creating localized
+strings in any gettext-based project. osTicket has opted to use a pure-php
+version of gettext in order to avoid possible pitfalls surrounding usage of
+gettext with PHP including
+
+  * MO file caching requiring HTTP server restart
+  * `gettext` missing from the PHP installation
+  * Requirement of locale pre-configuration on the server
+
+### Adding new strings
+
+Use a few function calls to get your text localized:
+
+  * `__('string')` localize the string to the current user preference
+  * `_N('string', 'strings', n)` localize a string with plural alternatives
+    to the current user locale preference.
+  * `_S('string')` localize the string to the system primary language
+  * `_NS('string', 'strings', n)` localize a string with plural alternatives
+    to the system primary language.
+  * `_L('string', locale)` localize a string to a named locale. This is
+    useful in a ticket thread, for instance, which may have a language
+    preference separate from both the user and the system primary language
+  * `_NL('string', 'strings', n, locale)` localize a string with plural
+    alternatives to a specific locale.
+  * `_P('context', 'string')` localize a string which may have the same
+    source text as another string but have a different context or usage. An
+    example would be `open` used as a noun, adjective, and verb.
+  * `_NP('context', 'string', 'strings', n)` localize a string with plural
+    alternatives which may have different contexts in the source language.
+
+In some cases, it is not possible to use a function to translate your
+string. For instance, if it is used a as a class constant or default value
+for a class variable. In such a case, a hint can be used to tell the POT
+scanner to translate the string for use elsewhere. As an example, one might
+set something like this up:
+
+```php
+    class A {
+        static $name = /* @trans */ 'Localized string';
+    }
+
+    print __(A::$name);
+```
+
+In this case the localized version of the class variable is translated when
+it is used — not when it is defined.
+
+The `* @trans *` text in comment is significant. Both the asterisks and the
+term `@trans` are used to signify that the phrase should be made
+translatable. The comment must also be placed immedately before or after the
+string without any other PHP symbols in between (like the semi-colon).
+
+### Adding context to your strings
+
+Your text may be ambiguous or unclear when it is viewed outside the context
+of the code in which it appears. The system allows adding of comments
+similar to the stock gettext tools. Any comments written directly beside
+(behind or in front of) a localized string will be captured with the string
+in the translation template. For instance
+
+```php
+    print __('Localized' /* These comments are exported */);
+```
+
+All types of PHP comments are supported. They must be placed inside the
+parentheses of the localization call. If using single-line comments, use
+multiple lines if necessary to define the call so that your single-line
+comments can be used. For instance:
+
+```php
+    print sprintf(__(
+        // Tokens will be <a> of <b> <object(s)>
+        '%1$d of %2$d %3$s'
+        ),
+        $a, $b,
+        _N('object', 'objects', $b)
+    );
+```
+
+### Building POT file for translations
+
+Use the command line to compile the POT file to standard out
+
+    php setup/cli/manage.php i18n make-pot > message.pot
+
+### Building language packs
+
+In an effort for the php version of gettext to offer similar performance to
+the extension counterpart, a variant of the MO file is used which is a PHP
+serialized array written to a file. The original MO file functions basically
+like a text array. In stead of searching through the MO file for each string
+to be translated, the original and translated texts are placed into a hash
+array for quick access and the hash array is serialized for the language
+pack. At runtime, the hash array is recreated from the export and the
+strings are quickly accessed from the PHP hash array.
+
+A MO file can be manually compiled using a command-line interface
+
+    php include/class.translation.php message.mo > message.mo.php
diff --git a/setup/inc/class.installer.php b/setup/inc/class.installer.php
index c008566f5546d829ce5375bdc69a0d6cd7c0bf2a..0c7ea1f5b2a7029062e40969e3312cccd4a8be08 100644
--- a/setup/inc/class.installer.php
+++ b/setup/inc/class.installer.php
@@ -47,62 +47,62 @@ class Installer extends SetupWizard {
 
         $this->errors=$f=array();
 
-        $f['name']          = array('type'=>'string',   'required'=>1, 'error'=>'Name required');
-        $f['email']         = array('type'=>'email',    'required'=>1, 'error'=>'Valid email required');
-        $f['fname']         = array('type'=>'string',   'required'=>1, 'error'=>'First name required');
-        $f['lname']         = array('type'=>'string',   'required'=>1, 'error'=>'Last name required');
-        $f['admin_email']   = array('type'=>'email',    'required'=>1, 'error'=>'Valid email required');
-        $f['username']      = array('type'=>'username', 'required'=>1, 'error'=>'Username required');
-        $f['passwd']        = array('type'=>'password', 'required'=>1, 'error'=>'Password required');
-        $f['passwd2']       = array('type'=>'string',   'required'=>1, 'error'=>'Confirm password');
-        $f['prefix']        = array('type'=>'string',   'required'=>1, 'error'=>'Table prefix required');
-        $f['dbhost']        = array('type'=>'string',   'required'=>1, 'error'=>'Hostname required');
-        $f['dbname']        = array('type'=>'string',   'required'=>1, 'error'=>'Database name required');
-        $f['dbuser']        = array('type'=>'string',   'required'=>1, 'error'=>'Username required');
-        $f['dbpass']        = array('type'=>'string',   'required'=>1, 'error'=>'password required');
+        $f['name']          = array('type'=>'string',   'required'=>1, 'error'=>__('Name required'));
+        $f['email']         = array('type'=>'email',    'required'=>1, 'error'=>__('Valid email required'));
+        $f['fname']         = array('type'=>'string',   'required'=>1, 'error'=>__('First name required'));
+        $f['lname']         = array('type'=>'string',   'required'=>1, 'error'=>__('Last name required'));
+        $f['admin_email']   = array('type'=>'email',    'required'=>1, 'error'=>__('Valid email required'));
+        $f['username']      = array('type'=>'username', 'required'=>1, 'error'=>__('Username required'));
+        $f['passwd']        = array('type'=>'password', 'required'=>1, 'error'=>__('Password required'));
+        $f['passwd2']       = array('type'=>'password', 'required'=>1, 'error'=>__('Confirm Password'));
+        $f['prefix']        = array('type'=>'string',   'required'=>1, 'error'=>__('Table prefix required'));
+        $f['dbhost']        = array('type'=>'string',   'required'=>1, 'error'=>__('Host name required'));
+        $f['dbname']        = array('type'=>'string',   'required'=>1, 'error'=>__('Database name required'));
+        $f['dbuser']        = array('type'=>'string',   'required'=>1, 'error'=>__('Username required'));
+        $f['dbpass']        = array('type'=>'string',   'required'=>1, 'error'=>__('Password required'));
 
         $vars = array_map('trim', $vars);
 
         if(!Validator::process($f,$vars,$this->errors) && !$this->errors['err'])
-            $this->errors['err']='Missing or invalid data - correct the errors and try again.';
+            $this->errors['err']=__('Missing or invalid data - correct the errors and try again.');
 
 
         //Staff's email can't be same as system emails.
         if($vars['admin_email'] && $vars['email'] && !strcasecmp($vars['admin_email'],$vars['email']))
-            $this->errors['admin_email']='Conflicts with system email above';
+            $this->errors['admin_email']=__('Conflicts with system email above');
         //Admin's pass confirmation.
         if(!$this->errors && strcasecmp($vars['passwd'],$vars['passwd2']))
-            $this->errors['passwd2']='passwords to not match!';
+            $this->errors['passwd2']=__('Password(s) do not match');
         //Check table prefix underscore required at the end!
         if($vars['prefix'] && substr($vars['prefix'], -1)!='_')
-            $this->errors['prefix']='Bad prefix. Must have underscore (_) at the end. e.g \'ost_\'';
+            $this->errors['prefix']=__('Bad prefix. Must have underscore (_) at the end. e.g \'ost_\'');
 
         //Make sure admin username is not very predictable. XXX: feels dirty but necessary
         if(!$this->errors['username'] && in_array(strtolower($vars['username']),array('admin','admins','username','osticket')))
-            $this->errors['username']='Bad username';
+            $this->errors['username']=__('Bad username');
 
         // Support port number specified in the hostname with a colon (:)
         list($host, $port) = explode(':', $vars['dbhost']);
         if ($port && is_numeric($port) && ($port < 1 || $port > 65535))
-            $this->errors['db'] = 'Invalid database port number';
+            $this->errors['db'] = __('Invalid database port number');
 
         //MYSQL: Connect to the DB and check the version & database (create database if it doesn't exist!)
         if(!$this->errors) {
             if(!db_connect($vars['dbhost'],$vars['dbuser'],$vars['dbpass']))
-                $this->errors['db']='Unable to connect to MySQL server. '.db_connect_error();
+                $this->errors['db']=sprintf(__('Unable to connect to MySQL server: %s'), db_connect_error());
             elseif(explode('.', db_version()) < explode('.', $this->getMySQLVersion()))
-                $this->errors['db']=sprintf('osTicket requires MySQL %s or better!',$this->getMySQLVersion());
+                $this->errors['db']=sprintf(__('osTicket requires MySQL %s or later!'),$this->getMySQLVersion());
             elseif(!db_select_database($vars['dbname']) && !db_create_database($vars['dbname'])) {
-                $this->errors['dbname']='Database doesn\'t exist';
-                $this->errors['db']='Unable to create the database.';
+                $this->errors['dbname']=__("Database doesn't exist");
+                $this->errors['db']=__('Unable to create the database.');
             } elseif(!db_select_database($vars['dbname'])) {
-                $this->errors['dbname']='Unable to select the database';
+                $this->errors['dbname']=__('Unable to select the database');
             } else {
                 //Abort if we have another installation (or table) with same prefix.
                 $sql = 'SELECT * FROM `'.$vars['prefix'].'config` LIMIT 1';
                 if(db_query($sql, false)) {
-                    $this->errors['err'] = 'We have a problem - another installation with same table prefix exists!';
-                    $this->errors['prefix'] = 'Prefix already in-use';
+                    $this->errors['err'] = __('We have a problem - another installation with same table prefix exists!');
+                    $this->errors['prefix'] = __('Prefix already in-use');
                 } else {
                     //Try changing charset and collation of the DB - no bigie if we fail.
                     db_query('ALTER DATABASE '.$vars['dbname'].' DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci', false);
@@ -115,36 +115,39 @@ class Installer extends SetupWizard {
 
         /*************** We're ready to install ************************/
         define('ADMIN_EMAIL',$vars['admin_email']); //Needed to report SQL errors during install.
-        define('PREFIX',$vars['prefix']); //Table prefix
-        Bootstrap::defineTables(PREFIX);
+        define('TABLE_PREFIX',$vars['prefix']); //Table prefix
+        Bootstrap::defineTables(TABLE_PREFIX);
         Bootstrap::loadCode();
 
         $debug = true; // Change it to false to squelch SQL errors.
 
         //Last minute checks.
         if(!file_exists($this->getConfigFile()) || !($configFile=file_get_contents($this->getConfigFile())))
-            $this->errors['err']='Unable to read config file. Permission denied! (#2)';
+            $this->errors['err']=__('Unable to read config file. Permission denied! (#2)');
         elseif(!($fp = @fopen($this->getConfigFile(),'r+')))
-            $this->errors['err']='Unable to open config file for writing. Permission denied! (#3)';
+            $this->errors['err']=__('Unable to open config file for writing. Permission denied! (#3)');
 
         else {
             $streams = DatabaseMigrater::getUpgradeStreams(INCLUDE_DIR.'upgrader/streams/');
             foreach ($streams as $stream=>$signature) {
                 $schemaFile = INC_DIR."streams/$stream/install-mysql.sql";
                 if (!file_exists($schemaFile) || !($fp2 = fopen($schemaFile, 'rb')))
-                    $this->errors['err'] = $stream
-                        . ': Internal Error - please make sure your download is the latest (#1)';
+                    $this->errors['err'] = sprintf(
+                        __('%s: Internal Error - please make sure your download is the latest (#1)'),
+                        $stream);
                 elseif (
                         // TODO: Make the hash algo configurable in the streams
                         //       configuration ( core : md5 )
                         !($hash = md5(fread($fp2, filesize($schemaFile))))
                         || strcasecmp($signature, $hash))
-                    $this->errors['err'] = $stream
-                        .': Unknown or invalid schema signature ('
-                        .$signature.' .. '.$hash.')';
+                    $this->errors['err'] = sprintf(
+                        __('%s: Unknown or invalid schema signature (%s .. %s)'),
+                        $stream,
+                        $signature, $hash);
                 elseif (!$this->load_sql_file($schemaFile, $vars['prefix'], true, $debug))
-                    $this->errors['err'] = $stream
-                        .': Error parsing SQL schema! Get help from developers (#4)';
+                    $this->errors['err'] = sprintf(
+                        __('%s: Error parsing SQL schema! Get help from developers (#4)'),
+                        $stream);
             }
         }
 
@@ -154,23 +157,25 @@ class Installer extends SetupWizard {
             $i18n = new Internationalization($vars['lang_id']);
             $i18n->loadDefaultData();
 
-            $sql='SELECT `id` FROM '.PREFIX.'sla ORDER BY `id` LIMIT 1';
+            Signal::send('system.install', $this);
+
+            $sql='SELECT `id` FROM '.TABLE_PREFIX.'sla ORDER BY `id` LIMIT 1';
             $sla_id_1 = db_result(db_query($sql, false));
 
-            $sql='SELECT `dept_id` FROM '.PREFIX.'department ORDER BY `dept_id` LIMIT 1';
+            $sql='SELECT `dept_id` FROM '.TABLE_PREFIX.'department ORDER BY `dept_id` LIMIT 1';
             $dept_id_1 = db_result(db_query($sql, false));
 
-            $sql='SELECT `tpl_id` FROM '.PREFIX.'email_template_group ORDER BY `tpl_id` LIMIT 1';
+            $sql='SELECT `tpl_id` FROM '.TABLE_PREFIX.'email_template_group ORDER BY `tpl_id` LIMIT 1';
             $template_id_1 = db_result(db_query($sql, false));
 
-            $sql='SELECT `group_id` FROM '.PREFIX.'groups ORDER BY `group_id` LIMIT 1';
+            $sql='SELECT `group_id` FROM '.TABLE_PREFIX.'groups ORDER BY `group_id` LIMIT 1';
             $group_id_1 = db_result(db_query($sql, false));
 
-            $sql='SELECT `value` FROM '.PREFIX.'config WHERE namespace=\'core\' and `key`=\'default_timezone_id\' LIMIT 1';
+            $sql='SELECT `value` FROM '.TABLE_PREFIX.'config WHERE namespace=\'core\' and `key`=\'default_timezone_id\' LIMIT 1';
             $default_timezone = db_result(db_query($sql, false));
 
             //Create admin user.
-            $sql='INSERT INTO '.PREFIX.'staff SET created=NOW() '
+            $sql='INSERT INTO '.TABLE_PREFIX.'staff SET created=NOW() '
                 .", isactive=1, isadmin=1, group_id='$group_id_1', dept_id='$dept_id_1'"
                 .", timezone_id='$default_timezone', max_page_size=25"
                 .', email='.db_input($vars['admin_email'])
@@ -179,25 +184,24 @@ class Installer extends SetupWizard {
                 .', username='.db_input($vars['username'])
                 .', passwd='.db_input(Passwd::hash($vars['passwd']));
             if(!db_query($sql, false) || !($uid=db_insert_id()))
-                $this->errors['err']='Unable to create admin user (#6)';
+                $this->errors['err']=__('Unable to create admin user (#6)');
         }
 
         if(!$this->errors) {
             //Create default emails!
             $email = $vars['email'];
             list(,$domain)=explode('@',$vars['email']);
-            $sql='INSERT INTO '.PREFIX.'email (`name`,`email`,`created`,`updated`) VALUES '
+            $sql='INSERT INTO '.TABLE_PREFIX.'email (`name`,`email`,`created`,`updated`) VALUES '
                     ." ('Support','$email',NOW(),NOW())"
                     .",('osTicket Alerts','alerts@$domain',NOW(),NOW())"
                     .",('','noreply@$domain',NOW(),NOW())";
             $support_email_id = db_query($sql, false) ? db_insert_id() : 0;
 
 
-            $sql='SELECT `email_id` FROM '.PREFIX."email WHERE `email`='alerts@$domain' LIMIT 1";
+            $sql='SELECT `email_id` FROM '.TABLE_PREFIX."email WHERE `email`='alerts@$domain' LIMIT 1";
             $alert_email_id = db_result(db_query($sql, false));
 
             //Create config settings---default settings!
-            //XXX: rename ostversion  helpdesk_* ??
             $defaults = array(
                 'default_email_id'=>$support_email_id,
                 'alert_email_id'=>$alert_email_id,
@@ -209,7 +213,7 @@ class Installer extends SetupWizard {
                 'helpdesk_title'=>$vars['name']);
             $config = new Config('core');
             if (!$config->updateAll($defaults))
-                $this->errors['err']='Unable to create config settings (#7)';
+                $this->errors['err']=__('Unable to create config settings').' (#7)';
 
             // Set company name
             require_once(INCLUDE_DIR.'class.company.php');
@@ -221,7 +225,7 @@ class Installer extends SetupWizard {
 				if ($stream != 'core') {
                     $config = new Config($stream);
                     if (!$config->update('schema_signature', $signature))
-                        $this->errors['err']='Unable to create config settings (#8)';
+                        $this->errors['err']=__('Unable to create config settings').' (#8)';
 				}
 			}
         }
@@ -239,14 +243,14 @@ class Installer extends SetupWizard {
         $configFile= str_replace('%CONFIG-PREFIX',$vars['prefix'],$configFile);
         $configFile= str_replace('%CONFIG-SIRI',Misc::randCode(32),$configFile);
         if(!$fp || !ftruncate($fp,0) || !fwrite($fp,$configFile)) {
-            $this->errors['err']='Unable to write to config file. Permission denied! (#5)';
+            $this->errors['err']=__('Unable to write to config file. Permission denied! (#5)');
             return false;
         }
         @fclose($fp);
 
         /************* Make the system happy ***********************/
 
-        $sql='UPDATE '.PREFIX."email SET dept_id=$dept_id_1";
+        $sql='UPDATE '.TABLE_PREFIX."email SET dept_id=$dept_id_1";
         db_query($sql, false);
 
         global $cfg;
@@ -268,8 +272,8 @@ class Installer extends SetupWizard {
         //TODO: create another personalized ticket and assign to admin??
 
         //Log a message.
-        $msg="Congratulations osTicket basic installation completed!\n\nThank you for choosing osTicket!";
-        $sql='INSERT INTO '.PREFIX.'syslog SET created=NOW(), updated=NOW(), log_type="Debug" '
+        $msg=__("Congratulations osTicket basic installation completed!\n\nThank you for choosing osTicket!");
+        $sql='INSERT INTO '.TABLE_PREFIX.'syslog SET created=NOW(), updated=NOW(), log_type="Debug" '
             .', title="osTicket installed!"'
             .', log='.db_input($msg)
             .', ip_address='.db_input($_SERVER['REMOTE_ADDR']);
diff --git a/setup/inc/file-missing.inc.php b/setup/inc/file-missing.inc.php
index c0e1994e83a20c2d7c98b0e09c4ec005fabf5cb9..a60aaade9164cbc21e498d1563b0909a559f4b31 100644
--- a/setup/inc/file-missing.inc.php
+++ b/setup/inc/file-missing.inc.php
@@ -2,29 +2,30 @@
 if(!defined('SETUPINC')) die('Kwaheri!');
 ?>
     <div id="main">
-            <h1 style="color:#FF7700;">Configuration file missing!</h1>
+            <h1 style="color:#FF7700;"><?php echo __('Configuration file missing!');?></h1>
             <div id="intro">
-             <p>osTicket installer requires ability to write to the configuration file, <b>include/ost-config.php</b>. A template copy is located in the include directory (<b>include/ost-sampleconfig.php</b>).
+             <p><?php echo __('osTicket installer requires ability to write to the configuration file, <b>include/ost-config.php</b>. A template copy is located in the include directory (<b>include/ost-sampleconfig.php</b>).');?>
              </p>
             </div>
-            <h3>Solution: <font color="red"><?php echo $errors['err']; ?></font></h3>
-            Rename the sample file <b>include/ost-sampleconfig.php</b> to <b>ost-config.php</b> and click continue below.
+            <h3><?php echo __('Solution');?>: <font color="red"><?php echo $errors['err']; ?></font></h3>
+            <?php echo __('Rename the sample file <b>include/ost-sampleconfig.php</b> to <b>ost-config.php</b> and click continue below.');?>
             <ul>
-                <li><b>CLI:</b><br><i>cp include/ost-sampleconfig.php include/ost-config.php</i></li>
-                <li><b>FTP:</b><br> </li>
-                <li><b>Cpanel:</b><br> </li>
+                <li><b><?php echo __('CLI');?>:</b><br><i>cp include/ost-sampleconfig.php include/ost-config.php</i></li>
+                <li><b><?php echo __('Windows PowerShell');?>:</b><br><i>Copy-Item -Path include\ost-sampleconfig.php -Destination include\ost-config.php</i></li>
+                <li><b><?php echo __('FTP');?>:</b><br> </li>
+                <li><b><?php echo __('Cpanel');?>:</b><br> </li>
             </ul>
-            <p>If sample config file is missing - please make sure you uploaded all files in 'upload' folder or refer to the <a target="_blank" href="http://osticket.com/wiki/Installation">Installation Guide</a></p>
+            <p><?php echo sprintf(__('If sample config file is missing - please make sure you uploaded all files in \'upload\' folder or refer to the %1$s Installation Guide %2$s'),'<a target="_blank" href="http://osticket.com/wiki/Installation">','</a>');?></p>
             <div id="bar">
                 <form method="post" action="install.php">
                     <input type="hidden" name="s" value="config">
-                    <input class="btn" type="submit" name="submit" value="Continue &raquo;">
+                    <input class="btn" type="submit" name="submit" value="<?php echo __('Continue');?> &raquo;">
                 </form>
             </div>
     </div>
     <div id="sidebar">
-            <h3>Need Help?</h3>
+            <h3><?php echo __('Need Help?');?></h3>
             <p>
-            If you are looking for a greater level of support, we provide <u>professional installation services</u> and commercial support with guaranteed response times, and access to the core development team. We can also help customize osTicket or even add new features to the system to meet your unique needs. <a target="_blank" href="http://osticket.com/support">Learn More!</a>
+            <?php echo __('If you are looking for a greater level of support, we provide <u>professional installation services</u> and commercial support with guaranteed response times, and access to the core development team. We can also help customize osTicket or even add new features to the system to meet your unique needs.');?> <a target="_blank" href="http://osticket.com/support"><?php echo __('Learn More!');?></a>
             </p>
     </div>
diff --git a/setup/inc/file-perm.inc.php b/setup/inc/file-perm.inc.php
index 7c2378a77c46be6ca1f3173a9697464dabb480a9..0640bd547f9d04fb18fccd6bbe955a715a91c618 100644
--- a/setup/inc/file-perm.inc.php
+++ b/setup/inc/file-perm.inc.php
@@ -2,31 +2,35 @@
 if(!defined('SETUPINC')) die('Kwaheri!');
 ?>
     <div id="main">
-            <h1 style="color:#FF7700;">Configuration file is not writable</h1>
+            <h1 style="color:#FF7700;"><?php echo __('Configuration file is not writable');?></h1>
             <div id="intro">
-             <p>
-             osTicket installer requires ability to write to the configuration file <b>include/ost-config.php</b>. 
+            <p> <?php
+            echo sprintf(
+                 __('osTicket installer requires ability to write to the configuration file %s'),
+                 '<b style="white-space:nowrap">include/ost-config.php</b>');?>
              </p>
             </div>
-            <h3>Solution: <font color="red"><?php echo $errors['err']; ?></font></h3>
-            Please follow the instructions below to give read and write access to the web server user.
+            <h3><?php echo __('Solution');?>: <font color="red"><?php echo $errors['err']; ?></font></h3>
+            <?php echo __('Please follow the instructions below to give read and write access to the web server user.');?>
             <ul>
-                <li><b>CLI</b>:<br><i>chmod 0666  include/ost-config.php</i></li>
-                <li><b>FTP</b>:<br>Using WS_FTP this would be right hand clicking on the fil, selecting chmod, and then giving all permissions to the file.</li>
-                <li><b>Cpanel</b>:<br>Click on the file, select change permission, and then giving all permissions to the file.</li>
+                <li><b><?php echo __('CLI');?></b>:<br><i class="ltr">chmod 0666  include/ost-config.php</i></li>
+                <li><b><?php echo __('Windows PowerShell');?></b>:<br><?php echo __('Add "Full Access" permission for the "Everyone" user'); ?><br>
+                <i class="ltr">icacls include\ost-config.php /grant 'Everyone:F'</i></li>
+                <li><b><?php echo __('FTP');?></b>:<br><?php echo __('Using WS_FTP this would be right hand clicking on the file, selecting chmod, and then giving all permissions to the file.');?></li>
+                <li><b><?php echo __('Cpanel');?></b>:<br><?php echo __('Click on the file, select change permission, and then giving all permissions to the file.');?></li>
             </ul>
 
-            <p><i>Don't worry! We'll remind you to take away the write access post-install</i>.</p>
+            <p><i><?php echo __("Don't worry! We'll remind you to take away the write access post-install");?></i>.</p>
             <div id="bar">
                 <form method="post" action="install.php">
                     <input type="hidden" name="s" value="config">
-                    <input class="btn"  type="submit" name="submit" value="Done? Continue &raquo;">
+                    <button class="btn"  type="submit" name="submit"><?php echo __('Done? Continue');?> &raquo;</button>
                 </form>
             </div>
     </div>
     <div id="sidebar">
-            <h3>Need Help?</h3>
+            <h3><?php echo __('Need Help?');?></h3>
             <p>
-            If you are looking for a greater level of support, we provide <u>professional installation services</u> and commercial support with guaranteed response times, and access to the core development team. We can also help customize osTicket or even add new features to the system to meet your unique needs. <a target="_blank" href="http://osticket.com/support">Learn More!</a>
+            <?php echo __('If you are looking for a greater level of support, we provide <u>professional installation services</u> and commercial support with guaranteed response times, and access to the core development team. We can also help customize osTicket or even add new features to the system to meet your unique needs.');?> <a target="_blank" href="http://osticket.com/support"><?php echo __('Learn More!');?></a>
             </p>
     </div>
diff --git a/setup/inc/file-unclean.inc.php b/setup/inc/file-unclean.inc.php
index 8847ead592cc50ebbb4db937a33cac2334b72278..d84fdefd1586b13623a1169fe75c45840d5bf9dc 100644
--- a/setup/inc/file-unclean.inc.php
+++ b/setup/inc/file-unclean.inc.php
@@ -2,17 +2,17 @@
 if(!defined('SETUPINC')) die('Kwaheri!');
 ?>
     <div id="main">
-            <h1 style="color:#FF7700;">osTicket is already installed?</h1>
+            <h1 style="color:#FF7700;"><?php echo __('osTicket is already installed?');?></h1>
             <div id="intro">
-             <p>Configuration file already changed - which could mean osTicket is already installed or the config file is currupted. If you are trying to upgrade osTicket, then go to <a href="../scp/admin.php" >Admin Panel</a>.</p>
+             <p><?php echo sprintf(__('Configuration file already changed - which could mean osTicket is already installed or the config file is currupted. If you are trying to upgrade osTicket, then go to %s Admin Panel %s.'), '<a href="../scp/admin.php" >', '</a>');?></p>
 
-             <p>If you believe this is in error, please try replacing the config file with a unchanged template copy and try again or get technical help.</p>
-             <p>Refer to the <a target="_blank" href="http://osticket.com/wiki/Installation">Installation Guide</a> on the wiki for more information.</p>
+             <p><?php echo __('If you believe this is in error, please try replacing the config file with a unchanged template copy and try again or get technical help.');?></p>
+             <p><?php echo sprintf(__('Refer to the %s Installation Guide %s on the wiki for more information.'), '<a target="_blank" href="http://osticket.com/wiki/Installation">', '</a>');?></p>
             </div>
     </div>
     <div id="sidebar">
-            <h3>Need Help?</h3>
+            <h3><?php echo __('Need Help?');?></h3>
             <p>
-            We provide <u>professional installation services</u> and commercial support with guaranteed response times, and access to the core development team. <a target="_blank" href="http://osticket.com/support">Learn More!</a>
+            <?php echo __('We provide <u>professional installation services</u> and commercial support with guaranteed response times, and access to the core development team.');?> <a target="_blank" href="http://osticket.com/support"><?php echo __('Learn More!');?></a>
             </p>
     </div>
diff --git a/setup/inc/header.inc.php b/setup/inc/header.inc.php
index ef8aed7e3da82dd02a3e8c4cd41d53b44380b44b..b7530d1ce8df63df7e5abe99b392629a075b1d27 100644
--- a/setup/inc/header.inc.php
+++ b/setup/inc/header.inc.php
@@ -1,10 +1,16 @@
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
     "http://www.w3.org/TR/html4/loose.dtd">
-<html>
+<html <?php
+if (($lang = Internationalization::getCurrentLanguage())
+        && ($info = Internationalization::getLanguageInfo($lang))
+        && (@$info['direction'] == 'rtl'))
+    echo 'dir="rtl" class="rtl"';
+?>>
 <head>
     <title><?php echo $wizard['title']; ?></title>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     <link rel="stylesheet" href="css/wizard.css">
+    <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/flags.css">
     <script type="text/javascript" src="../js/jquery-1.8.3.min.js"></script>
     <script type="text/javascript" src="js/tips.js"></script>
     <script type="text/javascript" src="js/setup.js"></script>
@@ -13,15 +19,31 @@
     <div id="wizard">
         <div id="header">
             <img id="logo" src="./images/<?php echo $wizard['logo']?$wizard['logo']:'logo.png'; ?>" width="280" height="72" alt="osTicket">
-            <ul>
-                <li class="info"><?php echo $wizard['tagline']; ?></li>
+            <div class="info"><?php echo $wizard['tagline']; ?></div>
+            <br/>
+            <ul class="links">
                 <li>
                    <?php
                    foreach($wizard['menu'] as $k=>$v)
                     echo sprintf('<a target="_blank" href="%s">%s</a> &mdash; ',$v,$k);
                    ?>
-                    <a target="_blank" href="http://osticket.com/contact-us">Contact Us</a>
+                    <a target="_blank" href="http://osticket.com/contact-us"><?php echo __('Contact Us');?></a>
                 </li>
             </ul>
+            <div class="flags">
+<?php
+if (($all_langs = Internationalization::availableLanguages())
+    && (count($all_langs) > 1)
+) {
+    foreach ($all_langs as $code=>$info) {
+        list($lang, $locale) = explode('_', $code);
+?>
+        <a class="flag flag-<?php echo strtolower($locale ?: $info['flag'] ?: $lang); ?>"
+            href="?<?php echo urlencode($_GET['QUERY_STRING']); ?>&amp;lang=<?php echo $code;
+            ?>" title="<?php echo Internationalization::getLanguageDescription($code); ?>">&nbsp;</a>
+<?php }
+} ?>
+            </div>
         </div>
+        <div class="clear"></div>
         <div id="content">
diff --git a/setup/inc/install-done.inc.php b/setup/inc/install-done.inc.php
index ad8fc342f9f7ed2733a74a5a3dd1d7fe00cd98e3..29ea15c0f411f434328dcdca56af405b2d2ab18f 100644
--- a/setup/inc/install-done.inc.php
+++ b/setup/inc/install-done.inc.php
@@ -1,48 +1,49 @@
 <?php if(!defined('SETUPINC')) die('Kwaheri!');
 $url=URL;
 
-?>    
+?>
     <div id="main">
-        <h1 style="color:green;">Congratulations!</h1>
+        <h1 style="color:green;"><?php echo __('Congratulations!');?></h1>
         <div id="intro">
-        <p>Your osTicket installation has been completed successfully. Your next step is to fully configure your new support ticket system for use, but before you get to it please take a minute to cleanup.</p>
-        
-        <h2>Config file permission:</h2>
-        Change permission of ost-config.php to remove write access as shown below.
+        <p><?php echo __('Your osTicket installation has been completed successfully. Your next step is to fully configure your new support ticket system for use, but before you get to it please take a minute to cleanup.');?></p>
+
+        <h2><?php echo __('Config file permission');?>:</h2>
+        <?php echo __('Change permission of ost-config.php to remove write access as shown below.');?>
         <ul>
-            <li><b>CLI</b>:<br><i>chmod 0644  include/ost-config.php</i></li>
-            <li><b>FTP</b>:<br>Using WS_FTP this would be right hand clicking on the file, selecting chmod, and then remove write access</li>
-            <li><b>Cpanel</b>:<br>Click on the file, select change permission, and then remove write access.</li>
+            <li><b><?php echo __('CLI');?></b>:<br><i>chmod 0644  include/ost-config.php</i></li>
+            <li><b><?php echo __('Windows PowerShell');?></b>:<br><i>icacls include\ost-config.php /reset</i></li>
+            <li><b><?php echo __('FTP');?></b>:<br><?php echo __('Using WS_FTP this would be right hand clicking on the file, selecting chmod, and then remove write access');?></li>
+            <li><b><?php echo __('Cpanel');?></b>:<br><?php echo __('Click on the file, select change permission, and then remove write access.');?></li>
         </ul>
         </div>
-        <p>Below, you'll find some useful links regarding your installation.</p>
+        <p><?php echo __('Below, you\'ll find some useful links regarding your installation.');?></p>
         <table border="0" cellspacing="0" cellpadding="5" width="580" id="links">
             <tr>
                     <td width="50%">
-                        <strong>Your osTicket URL:</strong><Br>
+                        <strong><?php echo __('Your osTicket URL');?>:</strong><Br>
                         <a href="<?php echo $url; ?>"><?php echo $url; ?></a>
                     </td>
                     <td width="50%">
-                        <strong>Your Staff Control Panel:</strong><Br>
+                        <strong><?php echo __('Your Staff Control Panel');?>:</strong><Br>
                         <a href="../scp/admin.php"><?php echo $url; ?>scp</a>
                     </td>
                 </tr>
                 <tr>
                     <td width="50%">
-                        <strong>osTicket Forums:</strong><Br>
+                        <strong><?php echo __('osTicket Forums');?>:</strong><Br>
                         <a href="#">http://osticket.com/forum/</a>
                     </td>
                     <td width="50%">
-                        <strong>osTicket Community Wiki:</strong><Br>
+                        <strong><?php echo __('osTicket Community Wiki');?>:</strong><Br>
                         <a href="#">http://osticket.com/wiki/</a>
                     </td>
                 </tr>
             </table>
-            <p><b>PS</b>: Don't just make customers happy, make happy customers!</p>
+            <p><b>PS</b>: <?php echo __("Don't just make customers happy, make happy customers!");?></p>
     </div>
     <div id="sidebar">
-            <h3>What's Next?</h3>
-            <p><b>Post-Install Setup</b>: You can now log in to <a href="../scp/admin.php" target="_blank">Admin Panel</a> with the username and password you created during the install process. After a successful log in, you can proceed with post-install setup. For complete and upto date guide see <a href="http://osticket.com/wiki/Post-Install_Setup_Guide" target="_blank">osTicket wiki</a></p>
+            <h3><?php echo __("What's Next?");?></h3>
+            <p><b><?php echo __('Post-Install Setup');?></b>: <?php echo sprintf(__('You can now log in to %1$s Admin Panel %2$s with the username and password you created during the install process. After a successful log in, you can proceed with post-install setup.'), '<a href="../scp/admin.php" target="_blank">','</a>'); echo sprintf(__('For complete and upto date guide see %1$s osTicket wiki %2$s'), '<a href="http://osticket.com/wiki/Post-Install_Setup_Guide" target="_blank">', '</a>');?></p>
 
-            <p><b>Commercial Support Available</b>: Don't let technical problems impact your osTicket implementation. Get guidance and hands-on expertise to address unique challenges and make sure your osTicket runs smoothly, efficiently, and securely. <a target="_blank" href="http://osticket.com/commercial-support">Learn More!</a></p>
+            <p><b><?php echo __('Commercial Support Available');?></b>: <?php echo __("Don't let technical problems impact your osTicket implementation. Get guidance and hands-on expertise to address unique challenges and make sure your osTicket runs smoothly, efficiently, and securely.");?> <a target="_blank" href="http://osticket.com/support"><?php echo __('Learn More!');?></a></p>
    </div>
diff --git a/setup/inc/install-prereq.inc.php b/setup/inc/install-prereq.inc.php
index fa86058ba3744075b8d933d0317d3f5d90aaee2b..4c3167e6b71e07a85a32eed73f7ef0ac4bcb57f5 100644
--- a/setup/inc/install-prereq.inc.php
+++ b/setup/inc/install-prereq.inc.php
@@ -4,43 +4,49 @@ if(!defined('SETUPINC')) die('Kwaheri!');
 ?>
 
     <div id="main">
-            <h1>Thank You for Choosing osTicket!</h1>
+            <h2><?php echo __('Thank You for Choosing osTicket!');?></h2>
             <div id="intro">
-             <p>We are delighted you have chosen osTicket for your customer support ticketing system!</p>
-            <p>The installer will guide you every step of the way in the installation process. You're minutes away from your awesome customer support system!</p>
+             <p><?php echo __('We are delighted you have chosen osTicket for your customer support ticketing system!');?></p>
+            <p><?php echo __("The installer will guide you every step of the way in the installation process. You're minutes away from your awesome customer support system!");?></p>
             </div>
-            <h2>Prerequisites.</h3>
-            <p>Before we begin, we'll check your server configuration to make sure you meet the minimum requirements to install and run osTicket.</p>
-            <h3>Required: <font color="red"><?php echo $errors['prereq']; ?></font></h3>
-            These items are necessary in order to install and use osTicket.
+            <h2><?php echo __('Prerequisites');?>.</h3>
+            <p><?php echo __("Before we begin, we'll check your server configuration to make sure you meet the minimum requirements to install and run osTicket.");?></p>
+            <h3><?php echo __('Required');?>: <font color="red"><?php echo $errors['prereq']; ?></font></h3>
+            <?php echo __('These items are necessary in order to install and use osTicket.');?>
             <ul class="progress">
                 <li class="<?php echo $installer->check_php()?'yes':'no'; ?>">
-                PHP v5.3 or greater - (<small><b><?php echo PHP_VERSION; ?></b></small>)</li>
+                <?php echo sprintf(__('%s or greater'), '<span class="ltr">PHP v5.3</span>');?> &mdash; <small class="ltr">(<b><?php echo PHP_VERSION; ?></b>)</small></li>
                 <li class="<?php echo $installer->check_mysql()?'yes':'no'; ?>">
-                MySQLi extension for PHP - (<small><b><?php echo extension_loaded('mysqli')?'module loaded':'missing!'; ?></b></small>)</li>
+                <?php echo __('MySQLi extension for PHP');?> &mdash; <small><b><?php
+                    echo extension_loaded('mysqli')?__('module loaded'):__('missing!'); ?></b></small></li>
             </ul>
-            <h3>Recommended:</h3>
-            You can use osTicket without these, but you may not be able to use all features.
+            <h3><?php echo __('Recommended');?>:</h3>
+            <?php echo __('You can use osTicket without these, but you may not be able to use all features.');?>
             <ul class="progress">
-                <li class="<?php echo extension_loaded('gd')?'yes':'no'; ?>">Gdlib extension</li>
-                <li class="<?php echo extension_loaded('imap')?'yes':'no'; ?>">PHP IMAP extension. <em>Required for mail fetching</em></li>
-                <li class="<?php echo extension_loaded('xml') ?'yes':'no'; ?>">PHP XML extension (for XML API)</li>
-                <li class="<?php echo extension_loaded('dom') ?'yes':'no'; ?>">PHP XML-DOM extension (for HTML email processing)</li>
-                <li class="<?php echo extension_loaded('json')?'yes':'no'; ?>">PHP JSON extension (faster performance)</li>
-                <li class="<?php echo extension_loaded('gettext')?'yes':'no'; ?>">Gettext is used for translations (faster performance)</li>
-                <li class="<?php echo extension_loaded('mbstring')?'yes':'no'; ?>">Mbstring is <b>strongly</b> recommended for all installations</li>
-                <li class="<?php echo extension_loaded('phar')?'yes':'no'; ?>">Phar is <b>strongly</b> recommended for plugins and language packs</li>
+                <li class="<?php echo extension_loaded('gd')?'yes':'no'; ?>">Gdlib <?php echo __('extension');?></li>
+                <li class="<?php echo extension_loaded('imap')?'yes':'no'; ?>">PHP IMAP <?php echo __('extension');?> &mdash; <em><?php
+                    echo __('Required for mail fetching');?></em></li>
+                <li class="<?php echo extension_loaded('xml') ?'yes':'no'; ?>">PHP XML <?php echo __('extension');?> <?php
+                    echo __('(for XML API)');?></li>
+                <li class="<?php echo extension_loaded('dom') ?'yes':'no'; ?>">PHP XML-DOM <?php echo __('extension');?> <?php
+                    echo __('(for HTML email processing)');?></li>
+                <li class="<?php echo extension_loaded('json')?'yes':'no'; ?>">PHP JSON <?php echo __('extension');?> <?php
+                    echo __('(faster performance)');?></li>
+                <li class="<?php echo extension_loaded('mbstring')?'yes':'no'; ?>">Mbstring <?php echo __('extension');?> &mdash; <?php
+                    echo __('recommended for all installations');?></li>
+                <li class="<?php echo extension_loaded('phar')?'yes':'no'; ?>">Phar <?php echo __('extension');?> &mdash; <?php
+                    echo __('recommended for plugins and language packs');?></li>
             </ul>
             <div id="bar">
                 <form method="post" action="install.php">
                     <input type="hidden" name="s" value="prereq">
-                    <input class="btn"  type="submit" name="submit" value="Continue &raquo;">
+                    <input class="btn"  type="submit" name="submit" value="<?php echo __('Continue');?> &raquo;">
                 </form>
             </div>
     </div>
     <div id="sidebar">
-            <h3>Need Help?</h3>
+            <h3><?php echo __('Need Help?');?></h3>
             <p>
-            If you are looking for a greater level of support, we provide <u>professional installation services</u> and commercial support with guaranteed response times, and access to the core development team. We can also help customize osTicket or even add new features to the system to meet your unique needs. <a target="_blank" href="http://osticket.com/support">Learn More!</a>
+            <?php echo __('If you are looking for a greater level of support, we provide <u>professional installation services</u> and commercial support with guaranteed response times, and access to the core development team. We can also help customize osTicket or even add new features to the system to meet your unique needs.');?> <a target="_blank" href="http://osticket.com/support"><?php echo __('Learn More!');?></a>
             </p>
     </div>
diff --git a/setup/inc/install.inc.php b/setup/inc/install.inc.php
index a14ea07bc9b593110e0b2d0f6f47f68894d24459..807be16fda249a64a3550be5bfc39d891a25b1fd 100644
--- a/setup/inc/install.inc.php
+++ b/setup/inc/install.inc.php
@@ -3,125 +3,125 @@ if(!defined('SETUPINC')) die('Kwaheri!');
 $info=($_POST && $errors)?Format::htmlchars($_POST):array('prefix'=>'ost_','dbhost'=>'localhost','lang_id'=>'en_US');
 ?>
 <div id="main" class="step2">
-    <h1>osTicket Basic Installation</h1>
-            <p>Please fill out the information below to continue your osTicket installation. All fields are required.</p>
+    <h1><?php echo __('osTicket Basic Installation'); ?></h1>
+        <p><?php echo __('Please fill out the information below to continue your osTicket installation. All fields are required.');?></p>
             <font class="error"><strong><?php echo $errors['err']; ?></strong></font>
             <form action="install.php" method="post" id="install">
                 <input type="hidden" name="s" value="install">
-                <h4 class="head system">System Settings</h4>
-                <span class="subhead">The URL of your helpdesk, its name, and the default system email address</span>
+                <h4 class="head system"><?php echo __('System Settings');?></h4>
+                <span class="subhead"><?php echo __('The URL of your helpdesk, its name, and the default system email address');?></span>
                 <div class="row">
-                    <label>Helpdesk URL:</label>
-                    <span><strong><?php echo URL; ?></strong></span>
+                    <label><?php echo __('Helpdesk URL');?>:</label>
+                    <span class="ltr"><strong><?php echo URL; ?></strong></span>
                 </div>
                 <div class="row">
-                    <label>Helpdesk Name:</label>
+                    <label><?php echo __('Helpdesk Name');?>:</label>
                     <input type="text" name="name" size="45" tabindex="1" value="<?php echo $info['name']; ?>">
                     <a class="tip" href="#helpdesk_name"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['name']; ?></font>
                 </div>
                 <div class="row">
-                    <label>Default Email:</label>
+                    <label><?php echo __('Default Email');?>:</label>
                     <input type="text" name="email" size="45" tabindex="2" value="<?php echo $info['email']; ?>">
                     <a class="tip" href="#system_email"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['email']; ?></font>
                 </div>
                 <div class="row">
-                    <label>Default Language:</label>
+                    <label><?php echo __('Primary Language');?>:</label>
 <?php $langs = Internationalization::availableLanguages(); ?>
                 <select name="lang_id">
 <?php foreach($langs as $l) {
     $selected = ($info['lang_id'] == $l['code']) ? 'selected="selected"' : ''; ?>
                     <option value="<?php echo $l['code']; ?>" <?php echo $selected;
-                        ?>><?php echo $l['desc']; ?></option>
+                        ?>><?php echo Internationalization::getLanguageDescription($l['code']); ?></option>
 <?php } ?>
                 </select>
                 <a class="tip" href="#default_lang"><i class="icon-question-sign help-tip"></i></a>
                 <font class="error">&nbsp;<?php echo $errors['lang_id']; ?></font>
                 </div>
 
-                <h4 class="head admin">Admin User</h4>
-                <span class="subhead">Your primary administrator account - you can add more users later.</span>
+                <h4 class="head admin"><?php echo __('Admin User');?></h4>
+                <span class="subhead"><?php echo __('Your primary administrator account - you can add more users later.');?></span>
                 <div class="row">
-                    <label>First Name:</label>
+                    <label><?php echo __('First Name');?>:</label>
                     <input type="text" name="fname" size="45" tabindex="3" value="<?php echo $info['fname']; ?>">
                     <a class="tip" href="#first_name"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['fname']; ?></font>
                 </div>
                 <div class="row">
-                    <label>Last Name:</label>
+                    <label><?php echo __('Last Name');?>:</label>
                     <input type="text" name="lname" size="45" tabindex="4" value="<?php echo $info['lname']; ?>">
                     <a class="tip" href="#last_name"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['lname']; ?></font>
                 </div>
                 <div class="row">
-                    <label>Email Address:</label>
+                    <label><?php echo __('Email Address');?>:</label>
                     <input type="text" name="admin_email" size="45" tabindex="5" value="<?php echo $info['admin_email']; ?>">
                     <a class="tip" href="#email"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['admin_email']; ?></font>
                 </div>
                 <div class="row">
-                    <label>Username:</label>
+                    <label><?php echo __('Username');?>:</label>
                     <input type="text" name="username" size="45" tabindex="6" value="<?php echo $info['username']; ?>" autocomplete="off">
                     <a class="tip" href="#username"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['username']; ?></font>
                 </div>
                 <div class="row">
-                    <label> Password:</label>
+                    <label><?php echo __('Password');?>:</label>
                     <input type="password" name="passwd" size="45" tabindex="7" value="<?php echo $info['passwd']; ?>" autocomplete="off">
                     <a class="tip" href="#password"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['passwd']; ?></font>
                 </div>
                 <div class="row">
-                    <label>Retype Password:</label>
+                    <label><?php echo __('Retype Password');?>:</label>
                     <input type="password" name="passwd2" size="45" tabindex="8" value="<?php echo $info['passwd2']; ?>">
                     <a class="tip" href="#password2"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['passwd2']; ?></font>
                 </div>
 
-                <h4 class="head database">Database Settings</h4>
-                <span class="subhead">Database connection information <font class="error"><?php echo $errors['db']; ?></font></span>
+                <h4 class="head database"><?php echo __('Database Settings');?></h4>
+                <span class="subhead"><?php echo __('Database connection information');?> <font class="error"><?php echo $errors['db']; ?></font></span>
                 <div class="row">
-                    <label>MySQL Table Prefix:</label>
+                    <label><?php echo __('MySQL Table Prefix');?>:</label>
                     <input type="text" name="prefix" size="45" tabindex="9" value="<?php echo $info['prefix']; ?>">
                     <a class="tip" href="#db_prefix"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['prefix']; ?></font>
                 </div>
                 <div class="row">
-                    <label>MySQL Hostname:</label>
+                    <label><?php echo __('MySQL Hostname');?>:</label>
                     <input type="text" name="dbhost" size="45" tabindex="10" value="<?php echo $info['dbhost']; ?>">
                     <a class="tip" href="#db_host"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['dbhost']; ?></font>
                 </div>
                 <div class="row">
-                    <label>MySQL Database:</label>
+                    <label><?php echo __('MySQL Database');?>:</label>
                     <input type="text" name="dbname" size="45" tabindex="11" value="<?php echo $info['dbname']; ?>">
                     <a class="tip" href="#db_schema"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['dbname']; ?></font>
                 </div>
                 <div class="row">
-                    <label>MySQL Username:</label>
+                    <label><?php echo __('MySQL Username');?>:</label>
                     <input type="text" name="dbuser" size="45" tabindex="12" value="<?php echo $info['dbuser']; ?>">
                     <a class="tip" href="#db_user"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['dbuser']; ?></font>
                 </div>
                 <div class="row">
-                    <label>MySQL Password:</label>
+                    <label><?php echo __('MySQL Password');?>:</label>
                     <input type="password" name="dbpass" size="45" tabindex="13" value="<?php echo $info['dbpass']; ?>">
                     <a class="tip" href="#db_password"><i class="icon-question-sign help-tip"></i></a>
                     <font class="error"><?php echo $errors['dbpass']; ?></font>
                 </div>
                 <br>
                 <div id="bar">
-                    <input class="btn" type="submit" value="Install Now" tabindex="14">
+                    <input class="btn" type="submit" value="<?php echo __('Install Now');?>" tabindex="14">
                 </div>
             </form>
     </div>
     <div>
-        <p><strong>Need Help?</strong> We provide <u>professional installation services</u> and commercial support. <a target="_blank" href="http://osticket.com/support">Learn More!</a></p>
+        <p><strong><?php echo __('Need Help?');?></strong> <?php echo __('We provide <u>professional installation services</u> and commercial support.');?> <a target="_blank" href="http://osticket.com/support"><?php echo __('Learn More!');?></a></p>
     </div>
     <div id="overlay"></div>
     <div id="loading">
-        <h4>Doing stuff!</h4>
-        Please wait... while we install your new support ticket system!
+        <h4><?php echo __('Doing stuff!');?></h4>
+        <?php echo __('Please wait... while we install your new support ticket system!');?>
     </div>
diff --git a/setup/inc/ost-sampleconfig.php b/setup/inc/ost-sampleconfig.php
index 8a1f3b98eed8b51f6cbd535a8c181bebb5b7f312..6421abbd48c9f90d035930390173405b2c06d768 100644
--- a/setup/inc/ost-sampleconfig.php
+++ b/setup/inc/ost-sampleconfig.php
@@ -4,7 +4,7 @@
 
     Static osTicket configuration file. Mainly useful for mysql login info.
     Created during installation process and shouldn't change even on upgrades.
-   
+
     Peter Rotich <peter@osticket.com>
     Copyright (c)  2006-2010 osTicket
     http://www.osticket.com
@@ -36,7 +36,7 @@ define('ADMIN_EMAIL','%ADMIN-EMAIL');
 
 #Mysql Login info
 define('DBTYPE','mysql');
-define('DBHOST','%CONFIG-DBHOST'); 
+define('DBHOST','%CONFIG-DBHOST');
 define('DBNAME','%CONFIG-DBNAME');
 define('DBUSER','%CONFIG-DBUSER');
 define('DBPASS','%CONFIG-DBPASS');
diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql
index 20c3dc3460c7cb3f323d1f6425e0844d9b6ffa3d..430a0b1ea44841ca299526a20fab8578bad4c8e0 100644
--- a/setup/inc/streams/core/install-mysql.sql
+++ b/setup/inc/streams/core/install-mysql.sql
@@ -61,6 +61,20 @@ CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%faq_topic` (
   PRIMARY KEY  (`faq_id`,`topic_id`)
 ) DEFAULT CHARSET=utf8;
 
+DROP TABLE IF EXISTS `%TABLE_PREFIX%sequence`;
+CREATE TABLE `%TABLE_PREFIX%sequence` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `name` varchar(64) DEFAULT NULL,
+  `flags` int(10) unsigned DEFAULT NULL,
+  `next` bigint(20) unsigned NOT NULL DEFAULT '1',
+  `increment` int(11) DEFAULT '1',
+  `padding` char(1) DEFAULT '0',
+  `updated` datetime NOT NULL,
+  PRIMARY KEY (`id`)
+-- InnoDB is intended here because transaction support is required for row
+-- locking
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
 DROP TABLE IF EXISTS `%TABLE_PREFIX%sla`;
 CREATE TABLE `%TABLE_PREFIX%sla` (
   `id` int(11) unsigned NOT NULL auto_increment,
@@ -153,10 +167,13 @@ CREATE TABLE `%TABLE_PREFIX%list` (
     `name` varchar(255) NOT NULL,
     `name_plural` varchar(255),
     `sort_mode` enum('Alpha', '-Alpha', 'SortCol') NOT NULL DEFAULT 'Alpha',
+    `masks` int(11) unsigned NOT NULL DEFAULT 0,
+    `type` VARCHAR( 16 ) NULL DEFAULT NULL,
     `notes` text,
     `created` datetime NOT NULL,
     `updated` datetime NOT NULL,
-    PRIMARY KEY (`id`)
+    PRIMARY KEY (`id`),
+    KEY `type` (`type`)
 ) DEFAULT CHARSET=utf8;
 
 DROP TABLE IF EXISTS `%TABLE_PREFIX%list_items`;
@@ -279,6 +296,7 @@ CREATE TABLE `%TABLE_PREFIX%filter` (
   `disable_autoresponder` tinyint(1) unsigned NOT NULL default '0',
   `canned_response_id` int(11) unsigned NOT NULL default '0',
   `email_id` int(10) unsigned NOT NULL default '0',
+  `status_id` int(10) unsigned NOT NULL default '0',
   `priority_id` int(10) unsigned NOT NULL default '0',
   `dept_id` int(10) unsigned NOT NULL default '0',
   `staff_id` int(10) unsigned NOT NULL default '0',
@@ -404,15 +422,19 @@ CREATE TABLE `%TABLE_PREFIX%help_topic` (
   `isactive` tinyint(1) unsigned NOT NULL default '1',
   `ispublic` tinyint(1) unsigned NOT NULL default '1',
   `noautoresp` tinyint(3) unsigned NOT NULL default '0',
-  `priority_id` tinyint(3) unsigned NOT NULL default '0',
-  `dept_id` tinyint(3) unsigned NOT NULL default '0',
+  `flags` int(10) unsigned DEFAULT '0',
+  `status_id` int(10) unsigned NOT NULL default '0',
+  `priority_id` int(10) unsigned NOT NULL default '0',
+  `dept_id` int(10) unsigned NOT NULL default '0',
   `staff_id` int(10) unsigned NOT NULL default '0',
   `team_id` int(10) unsigned NOT NULL default '0',
   `sla_id` int(10) unsigned NOT NULL default '0',
   `page_id` int(10) unsigned NOT NULL default '0',
   `form_id` int(10) unsigned NOT NULL default '0',
+  `sequence_id` int(10) unsigned NOT NULL DEFAULT '0',
   `sort` int(10) unsigned NOT NULL default '0',
   `topic` varchar(32) NOT NULL default '',
+  `number_format` varchar(32) DEFAULT NULL,
   `notes` text,
   `created` datetime NOT NULL,
   `updated` datetime NOT NULL,
@@ -467,7 +489,8 @@ CREATE TABLE `%TABLE_PREFIX%note` (
   `sort` int(11) unsigned NOT NULL DEFAULT 0,
   `created` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
   `updated` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
-  PRIMARY KEY (`id`)
+  PRIMARY KEY (`id`),
+  KEY `ext_id` (`ext_id`)
 ) DEFAULT CHARSET=utf8;
 
 DROP TABLE IF EXISTS `%TABLE_PREFIX%session`;
@@ -568,14 +591,15 @@ CREATE TABLE `%TABLE_PREFIX%ticket` (
   `number` varchar(20),
   `user_id` int(11) unsigned NOT NULL default '0',
   `user_email_id` int(11) unsigned NOT NULL default '0',
+  `status_id` int(10) unsigned NOT NULL default '0',
   `dept_id` int(10) unsigned NOT NULL default '0',
   `sla_id` int(10) unsigned NOT NULL default '0',
   `topic_id` int(10) unsigned NOT NULL default '0',
   `staff_id` int(10) unsigned NOT NULL default '0',
   `team_id` int(10) unsigned NOT NULL default '0',
   `email_id` int(11) unsigned NOT NULL default '0',
+  `flags` int(10) unsigned NOT NULL default '0',
   `ip_address` varchar(64) NOT NULL default '',
-  `status` enum('open','closed') NOT NULL default 'open',
   `source` enum('Web','Email','Phone','API','Other') NOT NULL default 'Other',
   `isoverdue` tinyint(1) unsigned NOT NULL default '0',
   `isanswered` tinyint(1) unsigned NOT NULL default '0',
@@ -590,8 +614,8 @@ CREATE TABLE `%TABLE_PREFIX%ticket` (
   KEY `user_id` (`user_id`),
   KEY `dept_id` (`dept_id`),
   KEY `staff_id` (`staff_id`),
-  KEY `team_id` (`staff_id`),
-  KEY `status` (`status`),
+  KEY `team_id` (`team_id`),
+  KEY `status_id` (`status_id`),
   KEY `created` (`created`),
   KEY `closed` (`closed`),
   KEY `duedate` (`duedate`),
@@ -650,6 +674,23 @@ CREATE TABLE `%TABLE_PREFIX%ticket_event` (
   KEY `ticket_stats` (`timestamp`, `state`)
 ) DEFAULT CHARSET=utf8;
 
+DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_status`;
+CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%ticket_status` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `name` varchar(60) NOT NULL DEFAULT '',
+  `state` varchar(16) DEFAULT NULL,
+  `mode` int(11) unsigned NOT NULL DEFAULT '0',
+  `flags` int(11) unsigned NOT NULL DEFAULT '0',
+  `sort` int(11) unsigned NOT NULL DEFAULT '0',
+  `properties` text NOT NULL,
+  `created` datetime NOT NULL,
+  `updated` datetime NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `name` (`name`),
+  KEY `state` (`state`)
+) DEFAULT CHARSET=utf8;
+
+
 DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_priority`;
 CREATE TABLE `%TABLE_PREFIX%ticket_priority` (
   `priority_id` tinyint(4) NOT NULL auto_increment,
@@ -675,7 +716,7 @@ CREATE TABLE `%TABLE_PREFIX%ticket_thread` (
   `poster` varchar(128) NOT NULL default '',
   `source` varchar(32) NOT NULL default '',
   `title` varchar(255),
-  `body` text NOT NULL,
+  `body` mediumtext NOT NULL,
   `format` varchar(16) NOT NULL default 'html',
   `ip_address` varchar(64) NOT NULL default '',
   `created` datetime NOT NULL,
diff --git a/setup/inc/subscribe.inc.php b/setup/inc/subscribe.inc.php
index 45dddf1d711d9f0e8795cc8bd9aea9fd891f7930..e60fd275fda1117e914e17d20ba5842d04b278a0 100644
--- a/setup/inc/subscribe.inc.php
+++ b/setup/inc/subscribe.inc.php
@@ -2,47 +2,47 @@
 $info=($_POST && $errors)?Format::htmlchars($_POST):$_SESSION['info'];
 ?>
     <div id="main">
-        <h1>Basic Installation Completed</h1>
-        <p>osTicket installation has been completed successfully.</p>
-        <h3 style="color:#FF7700;">Stay up to date: </h3>
-        It's important to keep your installation up to date. Get announcements, security updates and alerts delivered directly to you!
+        <h1><?php echo __('Basic Installation Completed');?></h1>
+        <p><?php echo __('osTicket installation has been completed successfully.');?></p>
+        <h3 style="color:#FF7700;"><?php echo __('Stay up to date');?>: </h3>
+        <?php echo __("It's important to keep your installation up to date. Get announcements, security updates and alerts delivered directly to you!");?>
         <br><br>
         <form action="install.php" method="post">
-            <input type="hidden" name="s" value="subscribe">        
+            <input type="hidden" name="s" value="subscribe">
                 <div class="row">
-                    <label>Full Name:</label>
+                    <label><?php echo __('Full Name');?>:</label>
                     <input type="text" name="name" size="30" value="<?php echo $info['name']; ?>">
                     <font color="red"><?php echo $errors['name']; ?></font>
                 </div>
                 <div class="row">
-                    <label>Email Address:</label>
+                    <label><?php echo __('Email Address');?>:</label>
                     <input type="text" name="email" size="30" value="<?php echo $info['email']; ?>">
                     <font color="red"><?php echo $errors['email']; ?></font>
                 </div>
                 <br>
                 <div class="row">
-                    <strong>I'd like to receive the following notifications: <font color="red"><?php echo $errors['notify']; ?></font></strong>
+                    <strong><?php echo __("I'd like to receive the following notifications");?>: <font color="red"><?php echo $errors['notify']; ?></font></strong>
                     <label style="width:500px">
-                        <input style="position:relative; top:4px; margin-right:10px" 
+                        <input style="position:relative; top:4px; margin-right:10px"
                             type="checkbox" name="news" value="1" <?php echo (!isset($info['news']) || $info['news'])?'checked="checked"':''; ?> >
-                            News &amp; Announcements</label>
+                            <?php echo __('News &amp; Announcements');?></label>
                     <label style="width:500px">
-                        <input style="position:relative; top:4px; margin-right:10px"  
+                        <input style="position:relative; top:4px; margin-right:10px"
                             type="checkbox" name="alerts" value="1" <?php echo (!isset($info['alerts']) || $info['alerts'])?'checked="checked"':''; ?>>
-                            Security Alerts</label>
+                            <?php echo __('Security Alerts');?></label>
                 </div>
                 <div id="bar">
-                    <input class="btn" type="submit" value="Keep me Updated">
-                    <a class="unstyled" href="install.php?s=ns">No thanks.</a>
+                    <input class="btn" type="submit" value="<?php echo __('Keep me Updated');?>">
+                    <a class="unstyled" href="install.php?s=ns"><?php echo __('No thanks.');?></a>
                 </div>
         </form>
     </div>
     <div id="sidebar">
-            <h3>Thank you!</h3>
+            <h3><?php echo __('Thank you!');?></h3>
             <p>
-                Once again, thank you for choosing osTicket as your new customer support platform! 
+                <?php echo __('Once again, thank you for choosing osTicket as your new customer support platform! ');?>
             </p>
             <p>
-               Launching a new customer support platform can be a daunting task. Let us get you started! We provide professional support services to help get osTicket up and running smoothly for your organization. <a target="_blank" href="http://osticket.com/support/professional_services.php">Learn More!</a>
+               <?php echo __('Launching a new customer support platform can be a daunting task. Let us get you started! We provide professional support services to help get osTicket up and running smoothly for your organization.');?> <a target="_blank" href="http://osticket.com/support/professional_services.php"><?php echo __('Learn More!');?></a>
             </p>
     </div>
diff --git a/setup/install.php b/setup/install.php
index 32db0a47a7a0354bd0068831f86b98a078afa7c6..4a348cf632d24b3c7c62a406d80fc1bf86ca898c 100644
--- a/setup/install.php
+++ b/setup/install.php
@@ -24,11 +24,11 @@ define('OSTICKET_CONFIGFILE','../include/ost-config.php'); //XXX: Make sure the
 
 $installer = new Installer(OSTICKET_CONFIGFILE); //Installer instance.
 $wizard=array();
-$wizard['title']='osTicket Installer';
-$wizard['tagline']='Installing osTicket '.$installer->getVersionVerbose();
+$wizard['title']=__('osTicket Installer');
+$wizard['tagline']=sprintf(__('Installing osTicket %s'),$installer->getVersionVerbose());
 $wizard['logo']='logo.png';
-$wizard['menu']=array('Installation Guide'=>'http://osticket.com/wiki/Installation',
-        'Get Professional Help'=>'http://osticket.com/support');
+$wizard['menu']=array(__('Installation Guide')=>'http://osticket.com/wiki/Installation',
+        __('Get Professional Help')=>'http://osticket.com/support');
 
 if($_POST && $_POST['s']) {
     $errors = array();
@@ -38,13 +38,13 @@ if($_POST && $_POST['s']) {
             if($installer->check_prereq())
                 $_SESSION['ost_installer']['s']='config';
             else
-                $errors['prereq']='Minimum requirements not met!';
+                $errors['prereq']=__('Minimum requirements not met!');
             break;
         case 'config':
             if(!$installer->config_exists())
-                $errors['err']='Configuration file does NOT exist. Follow steps below to add one.';
+                $errors['err']=__('Configuration file does NOT exist. Follow steps below to add one.');
             elseif(!$installer->config_writable())
-                $errors['err']='Write access required to continue';
+                $errors['err']=__('Write access required to continue');
             else
                 $_SESSION['ost_installer']['s']='install';
             break;
@@ -56,20 +56,20 @@ if($_POST && $_POST['s']) {
                 //TODO: Go to subscribe step.
                 $_SESSION['ost_installer']['s']='done';
             } elseif(!($errors=$installer->getErrors()) || !$errors['err']) {
-                $errors['err']='Error installing osTicket - correct the errors below and try again.';
+                $errors['err']=__('Error installing osTicket - correct the errors below and try again.');
             }
             break;
         case 'subscribe':
             if(!trim($_POST['name']))
-                $errors['name'] = 'Required';
+                $errors['name'] = __('Required');
 
             if(!$_POST['email'])
-                $errors['email'] = 'Required';
+                $errors['email'] = __('Required');
             elseif(!Validator::is_email($_POST['email']))
-                $errors['email'] = 'Invalid';
+                $errors['email'] = __('Invalid');
 
             if(!$_POST['alerts'] && !$_POST['news'])
-                $errors['notify'] = 'Check one or more';
+                $errors['notify'] = __('Check one or more');
 
             if(!$errors)
                 $_SESSION['ost_installer']['s'] = 'done';
diff --git a/setup/js/tips.js b/setup/js/tips.js
index af111e21ed7171f4e33b4f45256fc09be29d70d0..1e35d976897cfc3cd121f3a8cb0ad195eb55fecf 100644
--- a/setup/js/tips.js
+++ b/setup/js/tips.js
@@ -40,9 +40,11 @@ jQuery(function($) {
                     "left":x_pos + "px"
                 }),
             tip_timer = setTimeout(function() {
+                var rtl = $('html.rtl').length > 0;
                 $('.tip_box').remove();
                 $('body').append(the_tip.hide().fadeIn());
-                if ($(window).width() < tip_content.outerWidth() + the_tip.position().left) {
+                if ((rtl && ($(window).width() > tip_content.outerWidth() + the_tip.position().left))
+                        || (!rtl && ($(window).width() < tip_content.outerWidth() + the_tip.position().left))) {
                     the_tip.css({'left':x_pos-tip_content.outerWidth()-40+'px'});
                     tip_box.addClass('right');
                     tip_arrow.addClass('flip-x');
diff --git a/setup/setup.inc.php b/setup/setup.inc.php
index 4327ffed75f66ac42b3b8a9e4dc159ec87b8bfb4..035a8333d8786500f49e622a87763603b70ca52a 100644
--- a/setup/setup.inc.php
+++ b/setup/setup.inc.php
@@ -59,5 +59,17 @@ require_once(INCLUDE_DIR.'class.passwd.php');
 require_once(INCLUDE_DIR.'class.format.php');
 require_once(INCLUDE_DIR.'class.misc.php');
 require_once INCLUDE_DIR.'mysqli.php';
+require_once INCLUDE_DIR.'class.i18n.php';
+
+Internationalization::bootstrap();
+
+// Set browser-preferred language (if installed)
+require_once INCLUDE_DIR.'class.translation.php';
+
+// Support flags in the setup portal too
+if (isset($_GET['lang']) && $_GET['lang']) {
+    $_SESSION['client:lang'] = $_GET['lang'];
+}
+TextDomain::configureForUser();
 
 ?>
diff --git a/setup/test/run-tests.php b/setup/test/run-tests.php
index 326f8be18f9ba349b2d908eab241be1f60d817d4..2cf780068159380012cb28331b1a65c6b8aa0126 100644
--- a/setup/test/run-tests.php
+++ b/setup/test/run-tests.php
@@ -7,36 +7,16 @@ $selected_test = (isset($argv[1])) ? $argv[1] : false;
 
 require_once "tests/class.test.php";
 
-if (!function_exists('get_osticket_root_path')) {
-    function get_osticket_root_path() {
-        # Hop up to the root folder
-        $start = dirname(__file__);
-        for (;;) {
-            if (file_exists($start . '/main.inc.php')) break;
-            $start .= '/..';
-        }
-        return realpath($start);
-    }
-}
 $root = get_osticket_root_path();
 define('INCLUDE_DIR', "$root/include/");
 define('PEAR_DIR', INCLUDE_DIR."pear/");
 ini_set('include_path', './'.PATH_SEPARATOR.INCLUDE_DIR.PATH_SEPARATOR.PEAR_DIR);
 
-if (!function_exists('glob_recursive')) {
-    # Check PHP syntax across all php files
-    function glob_recursive($pattern, $flags = 0) {
-        $files = glob($pattern, $flags);
-        foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) {
-            $files = array_merge($files,
-                glob_recursive($dir.'/'.basename($pattern), $flags));
-        }
-        return $files;
-    }
-}
-
 $fails = array();
 
+require_once INCLUDE_DIR . 'class.i18n.php';
+Internationalization::bootstrap();
+
 function show_fails() {
     global $fails, $root;
     if ($fails) {
diff --git a/setup/test/tests/class.test.php b/setup/test/tests/class.test.php
index a7f6c2fa044da245b2912c15e395755059d351c9..5dd6097dcb127a1a3391d36361872ed0e6b5dec2 100644
--- a/setup/test/tests/class.test.php
+++ b/setup/test/tests/class.test.php
@@ -5,7 +5,7 @@ class Test {
     var $warnings = array();
     var $name = "";
 
-    var $third_party_paths = array(
+    static $third_party_paths = array(
         '/include/JSON.php',
         '/include/htmLawed.php',
         '/include/PasswordHash.php',
@@ -15,11 +15,11 @@ class Test {
         '/include/plugins/',
         '/include/h2o/',
         '/include/mpdf/',
+
+        # Includes in the core-plugins project
+        '/lib/',
     );
 
-    function Test() {
-        call_user_func_array(array($this, '__construct'), func_get_args());
-    }
     function __construct() {
         assert_options(ASSERT_CALLBACK, array($this, 'fail'));
         error_reporting(E_ALL & ~E_WARNING);
@@ -31,14 +31,13 @@ class Test {
     function teardown() {
     }
 
-    /*static*/
-    function getAllScripts($excludes=true) {
-        $root = get_osticket_root_path();
+    static function getAllScripts($excludes=true, $root=false) {
+        $root = $root ?: get_osticket_root_path();
         $scripts = array();
         foreach (glob_recursive("$root/*.php") as $s) {
             $found = false;
             if ($excludes) {
-                foreach ($this->third_party_paths as $p) {
+                foreach (self::$third_party_paths as $p) {
                     if (strpos($s, $p) !== false) {
                         $found = true;
                         break;
@@ -105,4 +104,28 @@ class Test {
         return $line;
     }
 }
+
+if (!function_exists('glob_recursive')) {
+    # Check PHP syntax across all php files
+    function glob_recursive($pattern, $flags = 0) {
+        $files = glob($pattern, $flags);
+        foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) {
+            $files = array_merge($files,
+                glob_recursive($dir.'/'.basename($pattern), $flags));
+        }
+        return $files;
+    }
+}
+
+if (!function_exists('get_osticket_root_path')) {
+    function get_osticket_root_path() {
+        # Hop up to the root folder
+        $start = dirname(__file__);
+        for (;;) {
+            if (file_exists($start . '/main.inc.php')) break;
+            $start .= '/..';
+        }
+        return realpath($start);
+    }
+}
 ?>
diff --git a/setup/test/tests/stubs.php b/setup/test/tests/stubs.php
index 9479e274486467ffd000bbe7be06690911faa39e..5e3904e62d1ba00ccf14b1537d851bb8a81f6d6c 100644
--- a/setup/test/tests/stubs.php
+++ b/setup/test/tests/stubs.php
@@ -10,6 +10,7 @@ class mysqli {
     function real_connect() {}
     function select_db() {}
     function set_charset() {}
+    function autocommit() {}
 }
 
 class mysqli_stmt {
@@ -92,6 +93,10 @@ class DateTimeZone {
 
 class Phar {
     static function isValidPharFilename() {}
+    function setStub() {}
+    function startBuffering() {}
+    function stopBuffering() {}
+    function setSignatureAlgorithm() {}
 }
 
 class ZipArchive {
@@ -105,4 +110,16 @@ class finfo {
     function file() {}
     function buffer() {}
 }
+
+class Locale {
+    function getDisplayName() {}
+}
+class IntlBreakIterator {
+    static function createWordInstance() {}
+    function setText() {}
+}
+
+class SqlFunction {
+    static function NOW() {}
+}
 ?>
diff --git a/setup/tips.html b/setup/tips.html
deleted file mode 100644
index daf02e96a7ce380bbac118399222127f3d54727e..0000000000000000000000000000000000000000
--- a/setup/tips.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<div id="t1">
-<b>Helpdesk Name</b>
-<p>The name of your support system e.g [Company Name] Support</p>
-</div>
-<div id="t2">
-<b>Default System Email</b>
-<p>Default email address e.g support@yourcompany.com - you can add more later!</p>
-</div>
-<div id="t3">
-<b>First Name</b>
-<p>Admin's first name</p>
-</div>
-<div id="t4">
-<b>Last Name</b>
-<p>Admin's last name</p>
-</div>
-<div id="t5">
-<b>Email Address</b>
-<p>Admin's personal email address. Must be different from system's default email.</p>
-</div>
-<div id="t6">
-<b>Username</b>
-<p>Admin's login name. Must be at least three (3) characters.</p>
-</div>
-<div id="t7">
-<b>Password</b>
-<p>Admin's password.  Must be five (5) characters or more.</p>
-</div>
-<div id="t8">
-<b>Confirm Password</b>
-<p>Retype admin's password. Must match.</p>
-</div>
-<div id="t9">
-<b>MySQL Table Prefix.</b>
-<p>osTicket requires table prefix in order to avoid possible table conflicts in a shared database.</p>
-</div>
-<div id="t10">
-<b>MySQL Hostname</b>
-<p>
-Most hosts use 'localhost' for local database hostname. Check with your
-host if localhost fails.
-</p>
-<p>
-Default port set in php.ini is assumed. A non-standard port number can be
-specified as <code>hostname:port</code>
-</p>
-</div>
-<div id="t11">
-<b>MySQL Database</b>
-<p>Name of the database osTicket will use.</p>
-</div>
-<div id="t12">
-<b>MySQL Username</b>
-<p>The MySQL user must have full rights to the database.</p>
-</div>
-<div id="t13">
-<b>MySQL Password</b>
-<p>MySQL password associated with above user.</p>
-</div>
diff --git a/setup/tips.php b/setup/tips.php
new file mode 100644
index 0000000000000000000000000000000000000000..f2722d1dc0382a707411a997a5dc8ebe0fa1cdf9
--- /dev/null
+++ b/setup/tips.php
@@ -0,0 +1,62 @@
+<html>
+<?php
+require_once('setup.inc.php');
+?>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+</head>
+<body>
+<div id="t1">
+<b><?php echo __('Helpdesk Name');?></b>
+<p><?php echo __('The name of your support system e.g [Company Name] Support');?></p>
+</div>
+<div id="t2">
+<b><?php echo __('Default System Email');?></b>
+<p><?php echo __('Default email address e.g support@yourcompany.com - you can add more later!');?></p>
+</div>
+<div id="t3">
+<b><?php echo __('First Name');?></b>
+<p><?php echo __("Admin's first name");?></p>
+</div>
+<div id="t4">
+<b><?php echo __('Last Name');?></b>
+<p><?php echo __("Admin's last name");?></p>
+</div>
+<div id="t5">
+<b><?php echo __('Email Address');?></b>
+<p><?php echo __("Admin's personal email address. Must be different from system's default email.");?></p>
+</div>
+<div id="t6">
+<b><?php echo __('Username');?></b>
+<p><?php echo __("Admin's login name. Must be at least three (3) characters.");?></p>
+</div>
+<div id="t7">
+<b><?php echo __('Password');?></b>
+<p><?php echo __("Admin's password.  Must be five (5) characters or more.");?></p>
+</div>
+<div id="t8">
+<b><?php echo __('Confirm Password');?></b>
+<p><?php echo __("Retype admin's password. Must match.");?></p>
+</div>
+<div id="t9">
+<b><?php echo __('MySQL Table Prefix.');?></b>
+<p><?php echo __('osTicket requires table prefix in order to avoid possible table conflicts in a shared database.');?></p>
+</div>
+<div id="t10">
+<b><?php echo __('MySQL Hostname');?></b>
+<p><?php echo __("Most hosts use 'localhost' for local database hostname. Check with your host if localhost fails. Default port set in php.ini is assumed.");?></p>
+</div>
+<div id="t11">
+<b><?php echo __('MySQL Database');?></b>
+<p><?php echo __('Name of the database osTicket will use.');?></p>
+</div>
+<div id="t12">
+<b><?php echo __('MySQL Username');?></b> 
+<p><?php echo __('The MySQL user must have full rights to the database.');?></p>
+</div>
+<div id="t13">
+<b><?php echo __('MySQL Password');?></b>
+<p><?php echo __('MySQL password associated with above user.');?></p>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/tickets.php b/tickets.php
index b2655fa1388c878616ae879b61be8fdf52ec83a6..5b15b0015870db4509d07bc3e7f8c7a3ea5b8a1f 100644
--- a/tickets.php
+++ b/tickets.php
@@ -25,9 +25,9 @@ require_once(INCLUDE_DIR.'class.json.php');
 $ticket=null;
 if($_REQUEST['id']) {
     if (!($ticket = Ticket::lookup($_REQUEST['id']))) {
-        $errors['err']='Unknown or invalid ticket ID.';
+        $errors['err']=__('Unknown or invalid ticket ID.');
     } elseif(!$ticket->checkUserAccess($thisclient)) {
-        $errors['err']='Unknown or invalid ticket.'; //Using generic message on purpose!
+        $errors['err']=__('Unknown or invalid ticket ID.'); //Using generic message on purpose!
         $ticket=null;
     }
 }
@@ -35,6 +35,10 @@ if($_REQUEST['id']) {
 if (!$ticket && $thisclient->isGuest())
     Http::redirect('view.php');
 
+$tform = TicketForm::objects()->one();
+$messageField = $tform->getField('message');
+$attachments = $messageField->getWidget()->getAttachments();
+
 //Process post...depends on $ticket object above.
 if($_POST && is_object($ticket) && $ticket->getId()):
     $errors=array();
@@ -42,9 +46,9 @@ if($_POST && is_object($ticket) && $ticket->getId()):
     case 'edit':
         if(!$ticket->checkUserAccess($thisclient) //double check perm again!
                 || $thisclient->getId() != $ticket->getUserId())
-            $errors['err']='Access Denied. Possibly invalid ticket ID';
+            $errors['err']=__('Access Denied. Possibly invalid ticket ID');
         elseif (!$cfg || !$cfg->allowClientUpdates())
-            $errors['err']='Access Denied. Client updates are currently disabled';
+            $errors['err']=__('Access Denied. Client updates are currently disabled');
         else {
             $forms=DynamicFormEntry::forTicket($ticket->getId());
             foreach ($forms as $form) {
@@ -56,17 +60,18 @@ if($_POST && is_object($ticket) && $ticket->getId()):
         if (!$errors) {
             foreach ($forms as $f) $f->save();
             $_REQUEST['a'] = null; //Clear edit action - going back to view.
-            $ticket->logNote('Ticket details updated', sprintf(
-                'Ticket details were updated by client %s &lt;%s&gt;',
+            $ticket->logNote(__('Ticket details updated'), sprintf(
+                __('Ticket details were updated by client %s &lt;%s&gt;'),
                 $thisclient->getName(), $thisclient->getEmail()));
         }
         break;
     case 'reply':
         if(!$ticket->checkUserAccess($thisclient)) //double check perm again!
-            $errors['err']='Access Denied. Possibly invalid ticket ID';
+            $errors['err']=__('Access Denied. Possibly invalid ticket ID');
 
         if(!$_POST['message'])
-            $errors['message']='Message required';
+
+            $errors['message']=__('Message required');
 
         if(!$errors) {
             //Everything checked out...do the magic.
@@ -74,26 +79,28 @@ if($_POST && is_object($ticket) && $ticket->getId()):
                     'userId' => $thisclient->getId(),
                     'poster' => (string) $thisclient->getName(),
                     'message' => $_POST['message']);
-            if($cfg->allowOnlineAttachments() && $_FILES['attachments'])
-                $vars['files'] = AttachmentFile::format($_FILES['attachments'], true);
+            $vars['cannedattachments'] = $attachments->getClean();
             if (isset($_POST['draft_id']))
                 $vars['draft_id'] = $_POST['draft_id'];
 
             if(($msgid=$ticket->postMessage($vars, 'Web'))) {
-                $msg='Message Posted Successfully';
+                $msg=__('Message Posted Successfully');
                 // Cleanup drafts for the ticket. If not closed, only clean
                 // for this staff. Else clean all drafts for the ticket.
                 Draft::deleteForNamespace('ticket.client.' . $ticket->getId());
+                // Drop attachments
+                $attachments->reset();
+                $tform->setSource(array());
             } else {
-                $errors['err']='Unable to post the message. Try again';
+                $errors['err']=__('Unable to post the message. Try again');
             }
 
         } elseif(!$errors['err']) {
-            $errors['err']='Error(s) occurred. Please try again';
+            $errors['err']=__('Error(s) occurred. Please try again');
         }
         break;
     default:
-        $errors['err']='Unknown action';
+        $errors['err']=__('Unknown action');
     }
     $ticket->reload();
 endif;
@@ -116,5 +123,6 @@ if($ticket && $ticket->checkUserAccess($thisclient)) {
 }
 include(CLIENTINC_DIR.'header.inc.php');
 include(CLIENTINC_DIR.$inc);
+print $tform->getMedia();
 include(CLIENTINC_DIR.'footer.inc.php');
 ?>