Index: includes/bootstrap.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v
retrieving revision 1.165
diff -u -p -r1.165 bootstrap.inc
--- includes/bootstrap.inc	8 May 2007 16:36:55 -0000	1.165
+++ includes/bootstrap.inc	24 May 2007 14:22:56 -0000
@@ -423,12 +423,32 @@ function variable_init($conf = array()) 
  *   The name of the variable to return.
  * @param $default
  *   The default value to use if this variable has never been set.
+ * @param $variable_language
+ *   Optional language code to use. Pass NULL to look up the default value
+ *   of the variable, omit to use the page language.
  * @return
  *   The value of the variable.
  */
-function variable_get($name, $default) {
-  global $conf;
-
+function variable_get($name, $default, $variable_language = -1) {
+  global $conf, $language;
+  
+  // Before variable_init(), $language is empty,
+  // but variable_get() might be called anyway.
+  if (!empty($language)) {
+    // -1 is not possible as a language code, but it's the default,
+    // and we override that with the current language.
+    if ($variable_language == -1) {
+      $variable_language = $language->language;
+    }
+    // Load variables in this language on-demand.
+    if (!isset($conf[$variable_language])) {
+      $conf[$variable_language] = array();
+      $conf[$variable_language] = module_invoke('locale', 'variable_init', $variable_language);
+    }
+    if (isset($conf[$variable_language][$name])) {
+      return $conf[$variable_language][$name];
+    }
+  }
   return isset($conf[$name]) ? $conf[$name] : $default;
 }
 
@@ -449,6 +469,8 @@ function variable_set($name, $value) {
   db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, serialize($value));
   db_unlock_tables();
 
+  module_invoke('locale', 'variable_update', $name, $value);
+
   cache_clear_all('variables', 'cache');
 
   $conf[$name] = $value;
@@ -464,6 +486,7 @@ function variable_del($name) {
   global $conf;
 
   db_query("DELETE FROM {variable} WHERE name = '%s'", $name);
+  module_invoke('locale', 'variable_update', $name);
   cache_clear_all('variables', 'cache');
 
   unset($conf[$name]);
@@ -999,7 +1022,7 @@ function get_t() {
  *  Choose a language for the current page, based on site and user preferences.
  */
 function drupal_init_language() {
-  global $language, $user;
+  global $language;
 
   // Ensure the language is correctly returned, even without multilanguage support.
   // Useful for eg. XML/HTML 'lang' attributes.
Index: includes/locale.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/locale.inc,v
retrieving revision 1.129
diff -u -p -r1.129 locale.inc
--- includes/locale.inc	22 May 2007 07:42:36 -0000	1.129
+++ includes/locale.inc	24 May 2007 14:22:58 -0000
@@ -733,51 +733,60 @@ function locale_translate_export_po_form
  * User interface for string editing.
  */
 function locale_translate_edit_form($lid) {
-  $languages = language_list();
-  unset($languages['en']);
-
-  $result = db_query('SELECT DISTINCT s.source, t.translation, t.language FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid WHERE s.lid = %d', $lid);
-  $form = array();
-  $form['translations'] = array('#tree' => TRUE);
-  while ($translation = db_fetch_object($result)) {
-    $orig = $translation->source;
 
-    // Approximate the number of rows in a textfield with a maximum of 10.
-    $rows = min(ceil(str_word_count($orig) / 12), 10);
-
-    $form['translations'][$translation->language] = array(
-      '#type' => 'textarea',
-      '#title' => $languages[$translation->language]->name,
-      '#default_value' => $translation->translation,
-      '#rows' => $rows,
-    );
-    unset($languages[$translation->language]);
-  }
-
-  // Handle erroneous lid.
-  if (!isset($orig)) {
+  // Fetch source string, if possible.
+  $source = db_fetch_object(db_query('SELECT source, textgroup, location FROM {locales_source} WHERE lid = %d', $lid));
+  if (!$source) {
     drupal_set_message(t('String not found.'), 'error');
     drupal_goto('admin/build/translate/search');
   }
 
-  // Add original text. Assign negative weight so that it floats to the top.
-  $form['item'] = array('#type' => 'item',
-    '#title' => t('Original text'),
-    '#value' => check_plain(wordwrap($orig, 0)),
-    '#weight' => -1,
+  // Add original text to the top and some values for form altering.
+  $form = array(
+    'original' => array(
+      '#type'  => 'item',
+      '#title' => t('Original text'),
+      '#value' => check_plain(wordwrap($source->source, 0)),
+    ),
+    'lid' => array(
+      '#type'  => 'value',
+      '#value' => $lid
+    ),
+    'textgroup' => array(
+      '#type'  => 'value',
+      '#value' => $source->textgroup,
+    ),
+    'location' => array(
+      '#type'  => 'value',
+      '#value' => $source->location
+    ),
   );
 
+  // Include default form controls with empty values for all languages.
+  // This ensures that the languages are always in the same order in forms.
+  $languages = language_list();
+  $default = language_default();
+  // Remove default language; that value is in $source.
+  unset($languages[($source->textgroup == 'default' ? 'en' : $default->language)]);
+  $form['translations'] = array('#tree' => TRUE);
+  // Approximate the number of rows to use in the default textarea.
+  $rows = min(ceil(str_word_count($source->source) / 12), 10);
   foreach ($languages as $langcode => $language) {
     $form['translations'][$langcode] = array(
       '#type' => 'textarea',
       '#title' => t($language->name),
       '#rows' => $rows,
+      '#default_value' => '',
     );
   }
 
-  $form['lid'] = array('#type' => 'value', '#value' => $lid);
-  $form['submit'] = array('#type' => 'submit', '#value' => t('Save translations'));
+  // Fetch translations and fill in default values in the form.
+  $result = db_query('SELECT DISTINCT translation, language FROM {locales_target} WHERE lid = %d', $lid);
+  while ($translation = db_fetch_object($result)) {
+    $form['translations'][$translation->language]['#default_value'] = $translation->translation;
+  }
 
+  $form['submit'] = array('#type' => 'submit', '#value' => t('Save translations'));
   return $form;
 }
 
@@ -787,6 +796,7 @@ function locale_translate_edit_form($lid
  */
 function locale_translate_edit_form_submit($form_values, $form, &$form_state) {
   $lid = $form_values['lid'];
+  
   foreach ($form_values['translations'] as $key => $value) {
     $trans = db_fetch_object(db_query("SELECT translation FROM {locales_target} WHERE lid = %d AND language = '%s'", $lid, $key));
     if (isset($trans->translation)) {
@@ -800,7 +810,12 @@ function locale_translate_edit_form_subm
 
   // Refresh the locale cache.
   locale_refresh_cache();
-
+  
+  // Clear variable cache if we edited a variable.
+  if ($form_values['textgroup'] == 'variable') {
+    locale_variable_refresh_cache();
+  }
+  
   $form_state['redirect'] = 'admin/build/translate/search';
   return;
 }
@@ -1776,7 +1791,7 @@ function _locale_translate_seek() {
 
   // We have at least one criterion to match
   if ($query = _locale_translate_seek_query()) {
-    $join = "SELECT s.source, s.location, s.lid, s.textgroup, t.translation, t.language FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid ";
+    $join = "SELECT s.source, s.location, s.lid, s.textgroup, t.translation, t.language FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid ";
 
     $arguments = array();
     // Compute LIKE section
Index: modules/locale/locale.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v
retrieving revision 1.174
diff -u -p -r1.174 locale.module
--- modules/locale/locale.module	22 May 2007 07:42:37 -0000	1.174
+++ modules/locale/locale.module	24 May 2007 14:23:02 -0000
@@ -480,3 +480,81 @@ function _locale_batch_import($filepath,
     $context['results'][] = $filepath;
   }
 }
+
+/**
+ * Update a variable source value for translation.
+ *
+ * @param $name
+ *   Variable name
+ * @param $value
+ *   Optional variable value. Will delete the variable
+ *   strings if NULL is passed.
+ */
+function locale_variable_update($name, $value = NULL) {
+  global $conf;
+  
+  // Get a list of all variables defined.
+  $variables = module_invoke_all('locale', 'variables');
+  foreach ($variables as $callback => $names) {
+    // Found the set of variables where this one is defined.
+    if (in_array($name, $names)) {
+      if (!isset($value)) {
+        $lid = db_result(db_query("SELECT lid FROM {locales_source} WHERE textgroup = 'variable' AND location = '%s'", $name));
+        if ($lid) {
+          db_query('DELETE FROM {locales_source} WHERE lid = %d', $lid);
+          db_query('DELETE FROM {locales_target} WHERE lid = %d', $lid);
+          locale_variable_refresh_cache();
+        }
+      }
+      else {
+        $lid = db_result(db_query("SELECT lid FROM {locales_source} WHERE textgroup = 'variable' AND location = '%s'", $name));
+        if ($lid) {
+          // We have this variable: modify value.
+          db_query("UPDATE {locales_source} SET source = '%s' WHERE lid = %d", $value, $lid);
+        }
+        else {
+          // No such variable: insert it.
+          db_query("INSERT INTO {locales_source} (location, textgroup, source) VALUES ('%s', 'variable', '%s')", $name, $value);
+        }
+      }
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+/**
+ * Remove the cached values for all settings to let them build up again later.
+ */
+function locale_variable_refresh_cache() {
+  $languages = language_list();
+  foreach ($languages as $langcode => $language) {
+    cache_clear_all('variables:'. $langcode, 'cache');
+  }
+}
+
+/**
+ * Return setting translations for a specific language.
+ */
+function locale_variable_init($langcode = NULL) {
+  $translated = array();
+  if ($cached = cache_get('variables:'. $langcode, 'cache')) {
+    $translated = $cached->data;
+  }
+  else {
+    $result = db_query("SELECT t.translation, s.location FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid WHERE s.textgroup = 'variable' AND t.language = '%s'", $langcode);
+    while ($variable = db_fetch_object($result)) {
+      $translated[$variable->location] = $variable->translation;
+    }
+    cache_set('variables:'. $langcode, $translated);
+  }
+
+  // Allow $conf[$langcode] to have variables translated too.
+  if (isset($conf[$langcode])) {
+    foreach ($conf[$langcode] as $name => $value) {
+      $translated[$name] = $value;
+    }
+  }
+  
+  return $translated;
+}
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.480
diff -u -p -r1.480 system.module
--- modules/system/system.module	22 May 2007 05:52:17 -0000	1.480
+++ modules/system/system.module	24 May 2007 14:23:02 -0000
@@ -540,11 +540,58 @@ function _system_zonelist() {
   return $zones;
 }
 
+/**
+ * Implementation of hook_locale().
+ */
+function system_locale($op = 'groups') {
+  switch ($op) {
+    case 'groups':
+      return array('variable' => t('Site settings'));
+    case 'variables':
+      return array(
+        'system_site_information_settings' => array(
+          'site_name', 'site_slogan', 'site_mission', 'site_footer', 'anonymous',
+        )
+      );
+  }
+}
+
+/**
+ * Implementation of hook_form_alter().
+ */
+function system_form_alter(&$form, $form_id) {
+  // If we are translating a site setting, provide custom form element.
+  if ($form_id == 'locale_translate_edit_form' && $form['textgroup']['#value'] == 'variable') {
+    // Get list of variables for localization and their form callbacks.
+    $variables = module_invoke_all('locale', 'variables');
+    $element = array();
+    foreach ($variables as $callback => $names) {
+      // Found the set of variables where this one is defined.
+      if (in_array($form['location']['#value'], $names)) {
+        // Grab settings form and extract the part we need now.
+        $settings_form = $callback();
+        $element = $settings_form[$form['location']['#value']];
+        $title = $element['#title'];
+        break;
+      }
+    }
+    // Found the element to use, replace textareas.
+    if (!empty($element)) {
+      $form['original']['#title'] = $title;
+      foreach ($form['translations'] as $language => $textarea) {
+        $element['#title'] = $textarea['#title'];
+        $element['#default_value'] = $textarea['#default_value'];
+        $form['translations'][$language] = $element;
+      }
+    }
+  }
+}
+
 function system_site_information_settings() {
   $form['site_name'] = array(
     '#type' => 'textfield',
     '#title' => t('Name'),
-    '#default_value' => variable_get('site_name', 'Drupal'),
+    '#default_value' => variable_get('site_name', 'Drupal', NULL),
     '#description' => t('The name of this website.'),
     '#required' => TRUE
   );
@@ -558,26 +605,26 @@ function system_site_information_setting
   $form['site_slogan'] = array(
     '#type' => 'textfield',
     '#title' => t('Slogan'),
-    '#default_value' => variable_get('site_slogan', ''),
+    '#default_value' => variable_get('site_slogan', '', NULL),
     '#description' => t('The slogan of this website. Some themes display a slogan when available.')
   );
 
   $form['site_mission'] = array(
     '#type' => 'textarea',
     '#title' => t('Mission'),
-    '#default_value' => variable_get('site_mission', ''),
+    '#default_value' => variable_get('site_mission', '', NULL),
     '#description' => t('Your site\'s mission statement or focus.')
   );
   $form['site_footer'] = array(
     '#type' => 'textarea',
     '#title' => t('Footer message'),
-    '#default_value' => variable_get('site_footer', ''),
+    '#default_value' => variable_get('site_footer', '', NULL),
     '#description' => t('This text will be displayed at the bottom of each page. Useful for adding a copyright notice to your pages.')
   );
   $form['anonymous'] = array(
     '#type' => 'textfield',
     '#title' => t('Anonymous user'),
-    '#default_value' => variable_get('anonymous', t('Anonymous')),
+    '#default_value' => variable_get('anonymous', t('Anonymous'), NULL),
     '#description' => t('The name used to indicate anonymous users.')
   );
   $form['site_frontpage'] = array(
