Added language export to JSON page and localization for Javascript using $lang.get(). Localized AJAX login interface.
authorDan
Sun, 28 Oct 2007 16:40:24 -0400
changeset 210 2b283402e4e4
parent 209 8a00247d1dee
child 211 753dabeca1ee
Added language export to JSON page and localization for Javascript using $lang.get(). Localized AJAX login interface.
includes/clientside/static/enano-lib-basic.js
includes/clientside/static/faders.js
includes/clientside/static/l10n.js
includes/clientside/static/misc.js
includes/functions.php
includes/lang.php
includes/template.php
language/english/enano.json
plugins/SpecialAdmin.php
plugins/SpecialUserFuncs.php
schema.sql
upgrade.sql
--- a/includes/clientside/static/enano-lib-basic.js	Sun Oct 28 14:32:13 2007 -0400
+++ b/includes/clientside/static/enano-lib-basic.js	Sun Oct 28 16:40:24 2007 -0400
@@ -277,6 +277,7 @@
   'toolbar.js',
   'windows.js',
   'rijndael.js',
+  'l10n.js',
   'template-compiler.js',
   'acl.js',
   'comments.js',
--- a/includes/clientside/static/faders.js	Sun Oct 28 14:32:13 2007 -0400
+++ b/includes/clientside/static/faders.js	Sun Oct 28 16:40:24 2007 -0400
@@ -441,7 +441,7 @@
 
 function mb_logout()
 {
-  var mb = new messagebox(MB_YESNO|MB_ICONQUESTION, 'Are you sure you want to log out?', 'If you log out, you will no longer be able to access your user preferences, your private messages, or certain areas of this site until you log in again.');
+  var mb = new messagebox(MB_YESNO|MB_ICONQUESTION, $lang.get('user_logout_confirm_title'), $lang.get('user_logout_confirm_body'));
   mb.onclick['Yes'] = function()
     {
       window.location = makeUrlNS('Special', 'Logout/' + title);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/includes/clientside/static/l10n.js	Sun Oct 28 16:40:24 2007 -0400
@@ -0,0 +1,60 @@
+/*
+ * Enano client-side localization library
+ */
+
+var Language = function(lang_id)
+{
+  if ( typeof(enano_lang) != 'object' )
+    return false;
+  if ( typeof(enano_lang[lang_id]) != 'object' )
+    return false;
+  this.strings = enano_lang[lang_id];
+  
+  this.get = function(string_id, subst)
+  {
+    var catname = string_id.substr(0, string_id.indexOf('_'));
+    var string_name = string_id.substr(string_id.indexOf('_') + 1);
+    if ( typeof(this.strings[catname]) != 'object' )
+      return string_id;
+    if ( typeof(this.strings[catname][string_name]) != 'string' )
+      return string_id;
+    return '[LJS] ' + this.perform_subst(this.strings[catname][string_name], subst);
+  }
+  
+  this.perform_subst = function(str, subst)
+  {
+    // var this_regex = /%this\.([a-z0-9_]+)%/;
+    // var match;
+    // while ( str.match(this_regex) )
+    // {
+    //   match = str.match(this_regex);
+    //   alert(match);
+    // }
+    // hackish workaround for %config.*%
+    str = str.replace(/%config\.([a-z0-9_]+)%/g, '%$1%');
+    if ( typeof(subst) == 'object' )
+    {
+      for ( var i in subst )
+      {
+        if ( !i.match(/^([a-z0-9_]+)$/) )
+          continue;
+        var regex = new RegExp('%' + i + '%', 'g');
+        str = str.replace(regex, subst[i]);
+      }
+    }
+    return str;
+  }
+  
+}
+
+var $lang;
+
+var language_onload = function()
+{
+  $lang = new Language(ENANO_LANG_ID);
+  // for debugging :-)
+  // alert( $lang.get('user_err_invalid_credentials_lockout_captcha', { lockout_fails: '3', lockout_threshold: '5', lockout_duration: '15' }) );
+}
+
+addOnloadHook(language_onload);
+
--- a/includes/clientside/static/misc.js	Sun Oct 28 14:32:13 2007 -0400
+++ b/includes/clientside/static/misc.js	Sun Oct 28 16:40:24 2007 -0400
@@ -311,38 +311,49 @@
   switch($data.error)
   {
     case 'key_not_found':
-      $errstring = 'Enano couldn\'t look up the encryption key used to encrypt your password. This most often happens if a cache rotation occurred during your login attempt, or if you refreshed the login page.';
+      $errstring = $lang.get('user_err_key_not_found');
       break;
     case 'key_wrong_length':
-      $errstring = 'The encryption key was the wrong length.';
+      $errstring = $lang.get('user_err_key_wrong_length');
       break;
     case 'too_big_for_britches':
-      $errstring = 'You are trying to authenticate at a level that your user account does not permit.';
+      $errstring = $lang.get('user_err_too_big_for_britches');
       break;
     case 'invalid_credentials':
-      $errstring = 'You have entered an invalid username or password. Please enter your login details again.';
+      $errstring = $lang.get('user_err_invalid_credentials');
+      var subst = {
+        lockout_fails: $data.lockout_fails,
+        lockout_threshold: $data.lockout_threshold,
+        lockout_duration: $data.lockout_duration
+      }
       if ( $data.lockout_policy == 'lockout' )
       {
-        $errstring += ' You have used up '+$data['lockout_fails']+' out of '+$data['lockout_threshold']+' login attempts. After you have used up all '+$data['lockout_threshold']+' login attempts, you will be locked out from logging in for '+$data['lockout_duration']+' minutes.';
+        $errstring += $lang.get('user_err_invalid_credentials_lockout', subst);
       }
       else if ( $data.lockout_policy == 'captcha' )
       {
-        $errstring += ' You have used up '+$data['lockout_fails']+' out of '+$data['lockout_threshold']+' login attempts. After you have used up all '+$data['lockout_threshold']+' login attempts, you will have to enter a visual confirmation code before logging in, effective for '+$data['lockout_duration']+' minutes.';
+        $errstring += $lang.get('user_err_invalid_credentials_lockout_captcha', subst);
       }
       break;
     case 'backend_fail':
-      $errstring = 'You entered the right credentials and everything was validated, but for some reason Enano couldn\'t register your session. This is an internal problem with the site and you are encouraged to contact site administration.';
+      $errstring = $lang.get('user_err_backend_fail');
       break;
     case 'locked_out':
       $attempts = parseInt($data['lockout_fails']);
       if ( $attempts > $data['lockout_threshold'])
         $attempts = $data['lockout_threshold'];
       $time_rem = $data.time_rem;
-      $s = ( $time_rem == 1 ) ? '' : 's';
-      $errstring = "You have used up all "+$data['lockout_threshold']+" allowed login attempts. Please wait "+$time_rem+" minute"+$s+" before attempting to log in again";
-      if ( $data['lockout_policy'] == 'captcha' )
-        $errstring += ', or enter the visual confirmation code shown above in the appropriate box';
-      $errstring += '.';
+      $s = ( $time_rem == 1 ) ? '' : $lang.get('meta_plural');
+      
+      var subst = {
+        lockout_threshold: $data.lockout_threshold,
+        time_rem: $time_rem,
+        plural: $s,
+        captcha_blurb: ( $data.lockout_policy == 'captcha' ? $lang.get('user_err_locked_out_captcha_blurb') : '' )
+      }
+      
+      $errstring = $lang.get('user_err_locked_out', subst);
+      
       break;
   }
   return $errstring;
@@ -358,11 +369,11 @@
     level = USER_LEVEL_MEMBER;
   ajax_auth_level_cache = level;
   var loading_win = '<div align="center" style="text-align: center;"> \
-      <p>Fetching an encryption key...</p> \
-      <p><small>Not working? Use the <a href="'+makeUrlNS('Special', 'Login/' + title)+'">alternate login form</a>.</p> \
+      <p>' + $lang.get('user_login_ajax_fetching_key') + '</p> \
+      <p><small>' + $lang.get('user_login_ajax_link_fullform', { link_full_form: makeUrlNS('Special', 'Login/' + title) }) + '</p> \
       <p><img alt="Please wait..." src="'+scriptPath+'/images/loading-big.gif" /></p> \
     </div>';
-  var title = ( level > USER_LEVEL_MEMBER ) ? 'You are requesting a sensitive operation.' : 'Please enter your username and password to continue.';
+  var title = ( level > USER_LEVEL_MEMBER ) ? $lang.get('user_login_ajax_prompt_title_elev') : $lang.get('user_login_ajax_prompt_title');
   ajax_auth_mb_cache = new messagebox(MB_OKCANCEL|MB_ICONLOCK, title, loading_win);
   ajax_auth_mb_cache.onbeforeclick['OK'] = ajaxValidateLogin;
   ajax_auth_mb_cache.onbeforeclick['Cancel'] = function()
@@ -416,13 +427,13 @@
         }
         else if ( level > USER_LEVEL_MEMBER )
         {
-          form_html += 'Please re-enter your login details, to verify your identity.<br /><br />';
+          form_html += $lang.get('user_login_ajax_prompt_body_elev') + '<br /><br />';
         }
         if ( ajax_auth_show_captcha )
          {
            var captcha_html = ' \
              <tr> \
-               <td>Code in image:</td> \
+               <td>' + $lang.get('user_login_field_captcha') + ':</td> \
                <td><input type="hidden" id="ajaxlogin_captcha_hash" value="' + ajax_auth_show_captcha + '" /><input type="text" tabindex="3" size="25" id="ajaxlogin_captcha_code" /> \
              </tr>';
          }
@@ -434,22 +445,22 @@
         form_html += ' \
           <table border="0" align="center"> \
             <tr> \
-              <td>Username:</td><td><input tabindex="1" id="ajaxlogin_user" type="text"     ' + disableme + 'size="25" /> \
+              <td>' + $lang.get('user_login_field_username') + ':</td><td><input tabindex="1" id="ajaxlogin_user" type="text"     ' + disableme + 'size="25" /> \
             </tr> \
             <tr> \
-              <td>Password:</td><td><input tabindex="2" id="ajaxlogin_pass" type="password" ' + disableme + 'size="25" /> \
+              <td>' + $lang.get('user_login_field_password') + ':</td><td><input tabindex="2" id="ajaxlogin_pass" type="password" ' + disableme + 'size="25" /> \
             </tr> \
             ' + captcha_html + ' \
             <tr> \
               <td colspan="2" style="text-align: center;"> \
-                <br /><small>Trouble logging in? Try the <a href="'+makeUrlNS('Special', 'Login/' + title, 'level=' + level)+'">full login form</a>.<br />';
+                <small>' + $lang.get('user_login_ajax_link_fullform', { link_full_form: makeUrlNS('Special', 'Login/' + title, 'level=' + level) }) + '<br />';
        if ( level <= USER_LEVEL_MEMBER )
        {
          form_html += ' \
-                Did you <a href="'+makeUrlNS('Special', 'PasswordReset')+'">forget your password</a>?<br /> \
-                Maybe you need to <a href="'+makeUrlNS('Special', 'Register')+'">create an account</a>.</small>';
+                ' + $lang.get('user_login_ajax_link_forgotpass', { forgotpass_link: makeUrlNS('Special', 'PasswordReset') }) + '<br /> \
+                ' + $lang.get('user_login_createaccount_blurb', { reg_link: makeUrlNS('Special', 'Register') });
        }
-       form_html += ' \
+       form_html += '</small> \
               </td> \
             </tr> \
           </table> \
@@ -588,7 +599,7 @@
   json_data = encodeURIComponent(json_data);
   
   var loading_win = '<div align="center" style="text-align: center;"> \
-      <p>Logging in...</p> \
+      <p>' + $lang.get('user_login_ajax_loggingin') + '</p> \
       <p><img alt="Please wait..." src="'+scriptPath+'/images/loading-big.gif" /></p> \
     </div>';
     
--- a/includes/functions.php	Sun Oct 28 14:32:13 2007 -0400
+++ b/includes/functions.php	Sun Oct 28 16:40:24 2007 -0400
@@ -273,15 +273,19 @@
  * @param string $timeout Timeout, in seconds, to delay the redirect. Defaults to 3.
  */
 
-function redirect($url, $title = 'Redirecting...', $message = 'Please wait while you are redirected.', $timeout = 3)
+function redirect($url, $title = 'etc_redirect_title', $message = 'etc_redirect_body', $timeout = 3)
 {
   global $db, $session, $paths, $template, $plugins; // Common objects
+  global $lang;
 
   if ( $timeout == 0 )
   {
     header('Location: ' . $url);
     header('HTTP/1.1 307 Temporary Redirect');
   }
+  
+  $title = $lang->get($title);
+  $message = $lang->get($message);
 
   $template->add_header('<meta http-equiv="refresh" content="' . $timeout . '; url=' . str_replace('"', '\\"', $url) . '" />');
   $template->add_header('<script type="text/javascript">
@@ -295,7 +299,12 @@
 
   $template->tpl_strings['PAGE_NAME'] = $title;
   $template->header(true);
-  echo '<p>' . $message . '</p><p>If you are not redirected within ' . ( $timeout + 1 ) . ' seconds, <a href="' . str_replace('"', '\\"', $url) . '">please click here</a>.</p>';
+  echo '<p>' . $message . '</p>';
+  $subst = array(
+      'timeout' => ( $timeout + 1 ),
+      'redirect_url' => str_replace('"', '\\"', $url)
+    );
+  echo '<p>' . $lang->get('etc_redirect_timeout', $subst) . '</p>';
   $template->footer(true);
 
   $db->close();
--- a/includes/lang.php	Sun Oct 28 14:32:13 2007 -0400
+++ b/includes/lang.php	Sun Oct 28 16:40:24 2007 -0400
@@ -36,6 +36,13 @@
    */
   
   var $lang_code;
+
+  /**
+   * Used to track when a language was last changed, to allow browsers to cache language data
+   * @var int
+   */
+  
+  var $lang_timestamp;
   
   /**
    * Will be an object that holds an instance of the class configured with the site's default language. Only instanciated when needed.
@@ -82,7 +89,7 @@
     }
     
     $lang_default = ( $x = getConfig('default_language') ) ? intval($x) : 'def';
-    $q = $db->sql_query("SELECT lang_id, lang_code, ( lang_id = $lang_default ) AS is_default FROM " . table_prefix . "language WHERE $sql_col OR lang_id = $lang_default ORDER BY is_default DESC LIMIT 1;");
+    $q = $db->sql_query("SELECT lang_id, lang_code, last_changed, ( lang_id = $lang_default ) AS is_default FROM " . table_prefix . "language WHERE $sql_col OR lang_id = $lang_default ORDER BY is_default DESC LIMIT 1;");
     
     if ( !$q )
       $db->_die('lang.php - main select query');
@@ -94,6 +101,7 @@
     
     $this->lang_id   = intval( $row['lang_id'] );
     $this->lang_code = $row['lang_code'];
+    $this->lang_timestamp = $row['last_changed'];
   }
   
   /**
@@ -302,6 +310,11 @@
     
     fwrite($handle, $exported . '; ?>');
     
+    // Update timestamp in database
+    $q = $db->sql_query('UPDATE ' . table_prefix . 'language SET last_changed = ' . time() . ' WHERE lang_id = ' . $this->lang_id . ';');
+    if ( !$q )
+      $db->_die('lang.php - updating timestamp on language');
+    
     // Done =)
     fclose($handle);
   }
--- a/includes/template.php	Sun Oct 28 14:32:13 2007 -0400
+++ b/includes/template.php	Sun Oct 28 16:40:24 2007 -0400
@@ -135,6 +135,7 @@
   {
     global $db, $session, $paths, $template, $plugins; // Common objects
     global $email;
+    global $lang;
     
     dc_here("template: initializing all variables");
     
@@ -632,6 +633,10 @@
     // Add the e-mail address client code to the header
     $this->add_header($email->jscode());
     
+    // Add language file
+    $lang_uri = makeUrlNS('Special', 'LangExportJSON/' . $lang->lang_id, false, true);
+    $this->add_header("<script type=\"text/javascript\" src=\"$lang_uri\"></script>");
+    
     // Generate the code for the Log out and Change theme sidebar buttons
     // Once again, the new template parsing system can be used here
     
@@ -710,7 +715,8 @@
             }
           }
       $js_dynamic .= '\';
-      var ENANO_CURRENT_THEME = \''. $session->theme .'\';';
+      var ENANO_CURRENT_THEME = \''. $session->theme .'\';
+      var ENANO_LANG_ID = ' . $lang->lang_id . ';';
       foreach($paths->nslist as $k => $c)
       {
         $js_dynamic .= "namespace_list['{$k}'] = '$c';";
--- a/language/english/enano.json	Sun Oct 28 14:32:13 2007 -0400
+++ b/language/english/enano.json	Sun Oct 28 16:40:24 2007 -0400
@@ -17,7 +17,7 @@
 
 var enano_lang = {
   categories: [
-    'meta', 'user', 'page', 'comment', 'onpage'
+    'meta', 'user', 'page', 'comment', 'onpage', 'etc'
   ],
   strings: {
     meta: {
@@ -31,15 +31,6 @@
     user: {
       login_message_short: 'Please enter your username and password to log in.',
       login_message_short_elev: 'Please re-enter your login details',
-      err_key_not_found: 'Enano couldn\'t look up the encryption key used to encrypt your password. This most often happens if a cache rotation occurred during your login attempt, or if you refreshed the login page.',
-      err_key_wrong_length: 'The encryption key was the wrong length.',
-      err_too_big_for_britches: 'You are trying to authenticate at a level that your user account does not permit.',
-      err_invalid_credentials: 'You have entered an invalid username or password. Please enter your login details again.',
-      err_invalid_credentials_lockout: ' You have used up %fails% out of %config.lockout_threshold% login attempts. After you have used up all %config.lockout_threshold% login attempts, you will be locked out from logging in for %config.lockout_duration% minutes.',
-      err_invalid_credentials_lockout_captcha: ' You have used up %lockout_fails% out of %config.lockout_threshold% login attempts. After you have used up all %config.lockout_threshold% login attempts, you will have to enter a visual confirmation code while logging in, effective for %config.lockout_duration% minutes.',
-      err_backend_fail: 'You entered the right credentials and everything was validated, but for some reason Enano couldn\'t register your session. This is an internal problem with the site and you are encouraged to contact site administration.',
-      err_locked_out: 'You have used up all %config.lockout_threshold% allowed login attempts. Please wait %time_rem% minute%plural% before attempting to log in again%config.captcha_blurb%.',
-      err_locked_out_captcha_blurb: ', or enter the visual confirmation code shown above in the appropriate box',
       login_body: 'Logging in enables you to use your preferences and access member information. If you don\'t have a username and password here, you can <a href="%reg_link%">create an account</a>.',
       login_body_elev: 'You are requesting that a sensitive operation be performed. To continue, please re-enter your password to confirm your identity.',
       login_field_username: 'Username',
@@ -47,9 +38,39 @@
       login_forgotpass_blurb: 'Forgot your password? <a href="%forgotpass_link%">No problem.</a>',
       login_createaccount_blurb: 'Maybe you need to <a href="%reg_link%">create an account</a>.',
       login_field_captcha: 'Code in image',
-      login_nocrypt_title: 'Important note regarding cryptography',
+      login_nocrypt_title: 'Important note regarding cryptography:',
       login_nocrypt_body: 'Some countries do not allow the import or use of cryptographic technology. If you live in one of the countries listed below, you should <a href="%nocrypt_link%">log in without using encryption</a>.',
       login_nocrypt_countrylist: 'This restriction applies to the following countries: Belarus, China, India, Israel, Kazakhstan, Mongolia, Pakistan, Russia, Saudi Arabia, Singapore, Tunisia, Venezuela, and Vietnam.',
+      login_usecrypt_title: 'Encryption is currently turned off.',
+      login_usecrypt_body: 'If you are not in one of the countries listed below, you should <a href="%usecrypt_link%">enable encryption</a> to secure the logon process.',
+      login_usecrypt_countrylist: 'The cryptography restriction applies to the following countries: Belarus, China, India, Israel, Kazakhstan, Mongolia, Pakistan, Russia, Saudi Arabia, Singapore, Tunisia, Venezuela, and Vietnam.',
+      login_success_title: 'Login successful',
+      login_success_body: 'You have successfully logged into the %config.site_name% site as "%username%". Redirecting to %redir_target%...',
+      
+      login_ajax_fetching_key: 'Fetching an encryption key...',
+      login_ajax_prompt_title: 'Please enter your username and password to continue.',
+      login_ajax_prompt_title_elev: 'You are requesting a sensitive operation.',
+      login_ajax_prompt_body_elev: 'Please re-enter your login details, to verify your identity.',
+      login_ajax_link_fullform: 'Trouble logging in? Try the <a href="%link_full_form%">full login form</a>.',
+      login_ajax_link_forgotpass: 'Did you <a href="%forgotpass_link%">forget your password</a>?',
+      login_ajax_loggingin: 'Logging in...',
+      
+      err_key_not_found: 'Enano couldn\'t look up the encryption key used to encrypt your password. This most often happens if a cache rotation occurred during your login attempt, or if you refreshed the login page.',
+      err_key_wrong_length: 'The encryption key was the wrong length.',
+      err_too_big_for_britches: 'You are trying to authenticate at a level that your user account does not permit.',
+      err_invalid_credentials: 'You have entered an invalid username or password. Please enter your login details again.',
+      err_invalid_credentials_lockout: ' You have used up %fails% out of %config.lockout_threshold% login attempts. After you have used up all %config.lockout_threshold% login attempts, you will be locked out from logging in for %config.lockout_duration% minutes.',
+      err_invalid_credentials_lockout_captcha: ' You have used up %lockout_fails% out of %config.lockout_threshold% login attempts. After you have used up all %config.lockout_threshold% login attempts, you will have to enter a visual confirmation code while logging in, effective for %config.lockout_duration% minutes.',
+      err_backend_fail: 'You entered the right credentials and everything was validated, but for some reason Enano couldn\'t register your session. This is an internal problem with the site and you are encouraged to contact site administration.',
+      err_locked_out: 'You have used up all %config.lockout_threshold% allowed login attempts. Please wait %time_rem% minute%plural% before attempting to log in again%captcha_blurb%.',
+      err_locked_out_captcha_blurb: ', or enter the visual confirmation code shown above in the appropriate box',
+      
+      logout_success_title: 'Logged out',
+      logout_success_body: 'You have been successfully logged out, and all cookies have been cleared. You will now be transferred to the main page.',
+      logout_confirm_title: 'Are you sure you want to log out?',
+      logout_confirm_body: 'If you log out, you will no longer be able to access your user preferences, your private messages, or certain areas of this site until you log in again.',
+      logout_confirm_title_elev: 'Are you sure you want to de-authenticate?',
+      logout_confirm_body_elev: 'If you de-authenticate, you will no longer be able to use the administration panel until you re-authenticate again. You may do so at any time using the Administration button on the sidebar.',
     },
     page: {
     },
@@ -57,6 +78,11 @@
     },
     admhome: {
     },
+    etc: {
+      redirect_title: 'Redirecting...',
+      redirect_body: 'Please wait while you are redirected.',
+      redirect_timeout: 'If you are not redirected within %timeout% seconds, please <a href="%redirect_url%">click here</a>.',
+    },
   }
 };
 
--- a/plugins/SpecialAdmin.php	Sun Oct 28 14:32:13 2007 -0400
+++ b/plugins/SpecialAdmin.php	Sun Oct 28 16:40:24 2007 -0400
@@ -2620,7 +2620,7 @@
       }
       if ( t == namespace_list.Admin + 'AdminLogout' )
       {
-        var mb = new messagebox(MB_YESNO|MB_ICONQUESTION, 'Are you sure you want to de-authenticate?', 'If you de-authenticate, you will no longer be able to use the administration panel until you re-authenticate again. You may do so at any time using the Administration button on the sidebar.');
+        var mb = new messagebox(MB_YESNO|MB_ICONQUESTION, $lang.get('user_logout_confirm_title_elev'), $lang.get('user_logout_confirm_body_elev'));
         mb.onclick['Yes'] = function() {
           var tigraentry = document.getElementById('i_div0_0').parentNode;
           var tigraobj = $(tigraentry);
--- a/plugins/SpecialUserFuncs.php	Sun Oct 28 14:32:13 2007 -0400
+++ b/plugins/SpecialUserFuncs.php	Sun Oct 28 16:40:24 2007 -0400
@@ -90,6 +90,14 @@
       \'namespace\'=>\'Special\',
       \'special\'=>0,\'visible\'=>1,\'comments_on\'=>0,\'protected\'=>1,\'delvotes\'=>0,\'delvote_ips\'=>\'\',
       ));
+      
+    $paths->add_page(Array(
+      \'name\'=>\'Language exporter\',
+      \'urlname\'=>\'LangExportJSON\',
+      \'namespace\'=>\'Special\',
+      \'special\'=>0,\'visible\'=>0,\'comments_on\'=>0,\'protected\'=>1,\'delvotes\'=>0,\'delvote_ips\'=>\'\',
+      ));
+      
     ');
 
 // function names are IMPORTANT!!! The name pattern is: page_<namespace ID>_<page URLname, without namespace>
@@ -305,18 +313,26 @@
            <?php
          }
          ?>
-         <?php if ( $level <= USER_LEVEL_MEMBER ) { ?>
          <tr>
            <td class="row3" colspan="3">
              <?php
-             $returnpage_link = ( $return = $paths->getAllParams() ) ? '/' . $return : '';
-             $nocrypt_link = makeUrlNS('Special', "Login$returnpage_link", "level=$level&use_crypt=0", true);
-             echo '<p><b>' . $lang->get('user_login_nocrypt_title') . ':</b> ' . $lang->get('user_login_nocrypt_body', array('nocrypt_link' => $nocrypt_link)) . '</p>';
-             echo '<p>' . $lang->get('user_login_nocrypt_countrylist') . '</p>';
+             if ( $level <= USER_LEVEL_MEMBER && ( !isset($_GET['use_crypt']) || ( isset($_GET['use_crypt']) && $_GET['use_crypt']!='0' ) ) )
+             {
+               $returnpage_link = ( $return = $paths->getAllParams() ) ? '/' . $return : '';
+               $nocrypt_link = makeUrlNS('Special', "Login$returnpage_link", "level=$level&use_crypt=0", true);
+               echo '<p><b>' . $lang->get('user_login_nocrypt_title') . '</b> ' . $lang->get('user_login_nocrypt_body', array('nocrypt_link' => $nocrypt_link)) . '</p>';
+               echo '<p>' . $lang->get('user_login_nocrypt_countrylist') . '</p>';
+             }
+             else if ( $level <= USER_LEVEL_MEMBER && ( isset($_GET['use_crypt']) && $_GET['use_crypt']=='0' ) )
+             {
+               $returnpage_link = ( $return = $paths->getAllParams() ) ? '/' . $return : '';
+               $usecrypt_link = makeUrlNS('Special', "Login$returnpage_link", "level=$level&use_crypt=1", true);
+               echo '<p><b>' . $lang->get('user_login_usecrypt_title') . '</b> ' . $lang->get('user_login_usecrypt_body', array('usecrypt_link' => $usecrypt_link)) . '</p>';
+               echo '<p>' . $lang->get('user_login_usecrypt_countrylist') . '</p>';
+             }
              ?>
            </td>
          </tr>
-         <?php } ?>
          <tr>
            <th colspan="3" style="text-align: center" class="subhead"><input type="submit" name="login" value="Log in" tabindex="<?php echo ( $level <= USER_LEVEL_MEMBER ) ? '3' : '2'; ?>" /></th>
          </tr>
@@ -348,6 +364,7 @@
 {
   global $db, $session, $paths, $template, $plugins; // Common objects
   global $__login_status;
+  global $lang;
   if ( isset($_GET['act']) && $_GET['act'] == 'ajaxlogin' )
   {
     $plugins->attachHook('login_password_reset', 'SpecialLogin_SendResponse_PasswordReset($row[\'user_id\'], $row[\'temp_password\']);');
@@ -403,7 +420,11 @@
       if(isset($_POST['return_to']))
       {
         $name = ( isset($paths->pages[$_POST['return_to']]['name']) ) ? $paths->pages[$_POST['return_to']]['name'] : $_POST['return_to'];
-        redirect( makeUrl($_POST['return_to'], false, true), 'Login successful', 'You have successfully logged into the '.getConfig('site_name').' site as "'.$session->username.'". Redirecting to ' . $name . '...' );
+        $subst = array(
+            'username' => $session->username,
+            'redir_target' => $name
+          );
+        redirect( makeUrl($_POST['return_to'], false, true), $lang->get('user_login_success_title'), $lang->get('user_login_success_body', $subst) );
       }
       else
       {
@@ -437,13 +458,15 @@
 
 function page_Special_Logout() {
   global $db, $session, $paths, $template, $plugins; // Common objects
+  global $lang;
   if ( !$session->user_logged_in )
     $paths->main_page();
   
   $l = $session->logout();
   if ( $l == 'success' )
   {
-    redirect(makeUrl(getConfig('main_page'), false, true), 'Logged out', 'You have been successfully logged out, and all cookies have been cleared. You will now be transferred to the main page.', 4);
+    
+    redirect(makeUrl(getConfig('main_page'), false, true), $lang->get('user_logout_success_title'), $lang->get('user_logout_success_body'), 4);
   }
   $template->header();
   echo '<h3>An error occurred during the logout process.</h3><p>'.$l.'</p>';
@@ -1631,4 +1654,31 @@
   }
 }
 
+function page_Special_LangExportJSON()
+{
+  global $db, $session, $paths, $template, $plugins; // Common objects
+  global $lang;
+  
+  $lang_id = ( $x = $paths->getParam(0) ) ? intval($x) : $lang->lang_id;
+  
+  if ( $lang->lang_id == $lang_id )
+    $lang_local =& $lang;
+  else
+    $lang_local = new Language($lang_id);
+  
+  $json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
+  
+  $timestamp = date('D, j M Y H:i:s T', $lang_local->lang_timestamp);
+  header("Last-Modified: $timestamp");
+  header("Date: $timestamp");
+  header('Content-type: text/javascript');
+  
+  $lang_local->fetch();
+  echo "if ( typeof(enano_lang) != 'object' )
+  var enano_lang = new Object();
+
+enano_lang[{$lang->lang_id}] = " . $json->encode($lang_local->strings) . ";";
+  
+}
+
 ?>
\ No newline at end of file
--- a/schema.sql	Sun Oct 28 14:32:13 2007 -0400
+++ b/schema.sql	Sun Oct 28 16:40:24 2007 -0400
@@ -272,6 +272,7 @@
   lang_code varchar(16) NOT NULL,
   lang_name_default varchar(64) NOT NULL,
   lang_name_native varchar(64) NOT NULL,
+  last_changed int(12) NOT NULL DEFAULT 0,
   PRIMARY KEY ( lang_id )
 ) CHARACTER SET `utf8`;
 
--- a/upgrade.sql	Sun Oct 28 14:32:13 2007 -0400
+++ b/upgrade.sql	Sun Oct 28 16:40:24 2007 -0400
@@ -9,7 +9,7 @@
 -- UPDATE {{TABLE_PREFIX}}group_members SET group_id=9998 WHERE group_id=4;
 -- INSERT INTO {{TABLE_PREFIX}}groups(group_id,group_name,group_type,system_group) VALUES(4, 'Regular members', 3, 1);
 CREATE TABLE {{TABLE_PREFIX}}lockout( id int(12) NOT NULL auto_increment, ipaddr varchar(40) NOT NULL, action ENUM('credential', 'level') NOT NULL DEFAULT 'credential', timestamp int(12) NOT NULL DEFAULT 0, PRIMARY KEY ( id ) ) CHARACTER SET `utf8`;
-CREATE TABLE {{TABLE_PREFIX}}language( lang_id smallint(5) NOT NULL auto_increment, lang_code varchar(16) NOT NULL, lang_name_default varchar(64) NOT NULL, lang_name_native varchar(64) NOT NULL, PRIMARY KEY ( lang_id ) ) CHARACTER SET `utf8`;
+CREATE TABLE {{TABLE_PREFIX}}language( lang_id smallint(5) NOT NULL auto_increment, lang_code varchar(16) NOT NULL, lang_name_default varchar(64) NOT NULL, lang_name_native varchar(64) NOT NULL, last_changed int(12) NOT NULL DEFAULT 0, PRIMARY KEY ( lang_id ) ) CHARACTER SET `utf8`;
 CREATE TABLE {{TABLE_PREFIX}}language_strings( string_id bigint(15) NOT NULL auto_increment, lang_id smallint(5) NOT NULL, string_category varchar(32) NOT NULL, string_name varchar(64) NOT NULL, string_content longtext NOT NULL, PRIMARY KEY ( string_id ) );
 ALTER TABLE {{TABLE_PREFIX}}users ADD COLUMN user_lang smallint(5) NOT NULL;
 ---END Stable1.0ToUnstable1.1---