diff -urp --strip-trailing-cr ../drupal-6.x-dev/includes/locale.inc ./includes/locale.inc
--- ../drupal-6.x-dev/includes/locale.inc	2007-11-11 17:14:45.000000000 +0100
+++ ./includes/locale.inc	2007-11-17 18:44:11.000000000 +0100
@@ -2416,22 +2416,31 @@ function _locale_get_predefined_list() {
  *   Language code to import translations for.
  * @param $finished
  *   Optional finished callback for the batch.
+ * @param $skip
+ *   Array of component names to skip. Used in the installer for the
+ *   second pass import, when most components are already imported.
  * @return
  *   A batch structure or FALSE if no files found.
  */
-function locale_batch_by_language($langcode, $finished = '_locale_batch_installer_finished') {
+function locale_batch_by_language($langcode, $finished = NULL, $skip = array()) {
   // Collect all files to import for all enabled modules and themes.
   $files = array();
-  $result = db_query("SELECT name, filename FROM {system} WHERE status = 1");
+  $components = array();
+  $query = "SELECT name, filename FROM {system} WHERE status = 1";
+  if (count($skip)) {
+    $query .= " AND name NOT IN (". db_placeholders($skip, 'varchar') .")"; 
+  }
+  $result = db_query($query, $skip);
   while ($component = db_fetch_object($result)) {
     // Collect all files for all components, names as $langcode.po or
     // with names ending with $langcode.po. This allows for filenames
     // like node-module.de.po to let translators use small files and
     // be able to import in smaller chunks.
     $files = array_merge($files, file_scan_directory(dirname($component->filename) .'/translations', '(^|\.)'. $langcode .'\.po$', array('.', '..', 'CVS'), 0, FALSE));
+    $components[] = $component->name;
   }
 
-  return _locale_batch_build($files, $finished);
+  return _locale_batch_build($files, $finished, $components);
 }
 
 /**
@@ -2474,10 +2483,12 @@ function locale_batch_by_component($comp
  *   Array of files to import
  * @param $finished
  *   Optional finished callback for the batch.
+ * @param $components
+ *   Optional list of component names the batch covers. Used in the installer.
  * @return
  *   A batch structure
  */
-function _locale_batch_build($files, $finished = NULL) {
+function _locale_batch_build($files, $finished = NULL, $components = array()) {
   $t = get_t();
   if (count($files)) {
     $operations = array();
@@ -2490,6 +2501,9 @@ function _locale_batch_build($files, $fi
         'init_message'  => $t('Starting import'),
         'error_message' => $t('Error importing interface translations'),
         'file'          => './includes/locale.inc',
+        // This is not a batch API construct, but data passed along to the
+        // installer, so we know what did we import already.
+        '#components'   => $components,
       );
       if (isset($finished)) {
         $batch['finished'] = $finished;
@@ -2538,13 +2552,5 @@ function _locale_batch_language_finished
 }
 
 /**
- * Finished callback of installer locale import batch.
- * Advance installer task to the finished screen.
- */
-function _locale_batch_installer_finished($success, $results) {
-  variable_set('install_task', 'finished');
-}
-
-/**
  * @} End of "locale-autoimport"
  */
diff -urp --strip-trailing-cr ../drupal-6.x-dev/install.php ./install.php
--- ../drupal-6.x-dev/install.php	2007-11-16 00:12:38.000000000 +0100
+++ ./install.php	2007-11-17 20:49:19.000000000 +0100
@@ -27,6 +27,9 @@ function install_main() {
   // Ensure correct page headers are sent (e.g. caching)
   drupal_page_header();
 
+  // Set up $language, so t() caller functions will still work.
+  drupal_init_language();
+
   // Check existing settings.php.
   $verify = install_verify_settings();
 
@@ -448,6 +451,9 @@ function install_select_profile() {
   }
 }
 
+/**
+ * Form API array definition for the profile selection form.
+ */
 function install_select_profile_form(&$form_state, $profiles) {
   foreach ($profiles as $profile) {
     include_once($profile->filename);
@@ -525,6 +531,19 @@ function install_select_locale($profilen
     return FALSE;
   }
   else {
+    // Allow profile to pre-select the language, skipping the selection.
+    $function = $profilename .'_profile_details';
+    if (function_exists($function)) {
+      $details = $function();
+      if (isset($details['language'])) {
+        foreach ($locales as $locale) {
+          if ($details['language'] == $locale->name) {
+            return $locale->name;
+          }
+        }
+      }
+    }
+
     foreach ($locales as $locale) {
       if ($_POST['locale'] == $locale->name) {
         return $locale->name;
@@ -540,6 +559,9 @@ function install_select_locale($profilen
   }
 }
 
+/**
+ * Form API array definition for language selection.
+ */
 function install_select_locale_form(&$form_state, $locales) {
   include_once './includes/locale.inc';
   $languages = _locale_get_predefined_list();
@@ -589,7 +611,7 @@ function install_already_done_error() {
 }
 
 /**
- * Tasks performed after the database is initialized. Called from install.php.
+ * Tasks performed after the database is initialized.
  */
 function install_tasks($profile, $task) {
   global $base_url, $install_locale;
@@ -599,40 +621,60 @@ function install_tasks($profile, $task) 
   drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
   $_SESSION['messages'] = $messages;
 
-  // Build a page for a final task.
+  // URL used to direct page requests.
+  $url = $base_url .'/install.php?locale='. $install_locale .'&profile='. $profile;
+
+  // Build a page for final tasks.
   drupal_maintenance_theme();
   if (empty($task)) {
-    variable_set('install_task', 'configure');
-    $task = 'configure';
+    variable_set('install_task', 'locale-initial-import');
+    $task = 'locale-initial-import';
   }
 
   // We are using a list of if constructs here to allow for
   // passing from one task to the other in the same request.
 
-  if ($task == 'configure') {
-    drupal_set_title(st('Configure site'));
-
-    // We break the form up so we can tell when it's been successfully
-    // submitted.
+  // Import interface translations for the enabled modules.
+  if ($task == 'locale-initial-import') {
+    if (!empty($install_locale) && ($install_locale != 'en')) {
+      include_once 'includes/locale.inc';
+      // Enable installation language as default site language.
+      locale_add_language($install_locale, NULL, NULL, NULL, NULL, NULL, 1, TRUE);
+      // Collect files to import for this language.
+      $batch = locale_batch_by_language($install_locale, '_install_locale_initial_batch_finished');
+      if (!empty($batch)) {
+        // Remember components we cover in this batch set.
+        variable_set('install_locale_batch_components', $batch['#components']);        
+        // Start a batch, switch to 'locale-batch' task. We need to
+        // set the variable here, because batch_process() redirects.
+        variable_set('install_task', 'locale-initial-batch');
+        batch_set($batch);
+        batch_process($url, $url);
+      }
+    }
+    // Found nothing to import or not foreign language, go to next task.
+    $task = 'configure';
+  }
 
-    $form_state = array('storage' => NULL, 'submitted' => FALSE);
+  // We are running a batch import of interface translation files.
+  // This might run in multiple HTTP requests, constantly redirecting
+  // to the same address, until the batch finished callback is invoked
+  // and the task advances to 'configure'.
+  if ($task == 'locale-initial-batch') {
+    include_once 'includes/batch.inc';
+    include_once 'includes/locale.inc';
+    $output = _batch_page();
+  }
 
-    $form = drupal_retrieve_form('install_configure_form', $form_state);
-    $form_build_id = md5(mt_rand());
-    $form['#build_id'] = $form_build_id;
-    drupal_prepare_form('install_configure_form', $form, $form_state);
+  if ($task == 'configure') {
+    $form = drupal_get_form('install_configure_form', $url);
 
-    // Is the form submitted?
-    if (!empty($_POST) && $_POST['form_id'] == 'install_configure_form') {
-      $form['#post'] = $_POST;
-    }
-    else {
-      $form['#post'] = array();
-    }
+    if (!variable_get('install_configure_finished', FALSE)) {
+      // Not submitted yet: Prepare to display the form.
+      $output = $form;
+      drupal_set_title(st('Configure site'));
 
-    drupal_process_form('install_configure_form', $form, $form_state);
-    if (empty($form_state['redirect'])) {
-      // Add JavaScript validation for form.
+      // Add JavaScript validation.
       _user_password_dynamic_validation();
       drupal_add_js(drupal_get_path('module', 'system') .'/system.js', 'module');
       // We add these strings as settings because JavaScript translation does not
@@ -646,17 +688,17 @@ if (Drupal.jsEnabled) {
     Drupal.setDefaultTimezone();
   });
 }', 'inline');
-
       // Build menu to allow clean URL check.
       menu_rebuild();
-      $output = drupal_render_form('install_configure_form', $form);
     }
+
     else {
+      variable_del('install_configure_finished');
       $task = 'profile';
     }
   }
 
-  // If found an unknown task or the 'profile-custom' task, which is
+  // If found an unknown task or the 'profile' task, which is
   // reserved for profiles, hand over the control to the profile,
   // so it can run any number of custom tasks it defines.
   if (!in_array($task, install_reserved_tasks())) {
@@ -664,44 +706,38 @@ if (Drupal.jsEnabled) {
     if (function_exists($function)) {
       // The profile needs to run more code, maybe even more tasks.
       // $task is sent through as a reference and may be changed!
-      $output = $function($task);
+      $output = $function($task, $url);
     }
 
     // If the profile doesn't move on to a new task we assume
-    // that it is done: we let the installer regain control and
-    // proceed with the locale import.
+    // that it is done.
     if ($task == 'profile') {
-      $task = 'locale-import';
+      $task = 'profile-finished';
     }
   }
 
-  // Import interface translations for the enabled modules, after
-  // any changes made by the profile through the profile forms.
-  if ($task == 'locale-import') {
+  // Profile custom tasks are done, so let the installer regain
+  // control and proceed with importing the remaining translations.
+  if ($task == 'profile-finished') {
     if (!empty($install_locale) && ($install_locale != 'en')) {
       include_once 'includes/locale.inc';
-      // Enable installation language as default site language.
-      locale_add_language($install_locale, NULL, NULL, NULL, NULL, NULL, 1, TRUE);
-      // Collect files to import for this language.
-      $batch = locale_batch_by_language($install_locale);
+      // Collect files to import for this language. Skip components
+      // already covered in the initial batch set.
+      $batch = locale_batch_by_language($install_locale, '_install_locale_remaining_batch_finished', variable_get('install_locale_batch_components', array()));
+      // Remove temporary variable.
+      variable_del('install_locale_batch_components');
       if (!empty($batch)) {
-        // Start a batch, switch to 'locale-batch' task. We need to
+        // Start a batch, switch to 'locale-remaining-batch' task. We need to
         // set the variable here, because batch_process() redirects.
-        variable_set('install_task', 'locale-batch');
+        variable_set('install_task', 'locale-remaining-batch');
         batch_set($batch);
-        $path = $base_url .'/install.php?locale='. $install_locale .'&profile='. $profile;
-        batch_process($path, $path);
+        batch_process($url, $url);
       }
     }
     // Found nothing to import or not foreign language, go to next task.
     $task = 'finished';
   }
-
-  // We are running a batch import of interface translation files.
-  // This might run in multiple HTTP requests, constantly redirecting
-  // to the same address, until the batch finished callback is invoked
-  // and the task advances to 'finished'.
-  if ($task == 'locale-batch') {
+  if ($task == 'locale-remaining-batch') {
     include_once 'includes/batch.inc';
     include_once 'includes/locale.inc';
     $output = _batch_page();
@@ -740,10 +776,26 @@ if (Drupal.jsEnabled) {
 }
 
 /**
+ * Finished callback for the first locale import batch.
+ * Advance installer task to the configure screen.
+ */
+function _install_locale_initial_batch_finished($success, $results) {
+  variable_set('install_task', 'configure');
+}
+
+/**
+ * Finished callback for the second locale import batch.
+ * Advance installer task to the finished screen.
+ */
+function _install_locale_remaining_batch_finished($success, $results) {
+  variable_set('install_task', 'finished');
+}
+
+/**
  * The list of reserved tasks to run in the installer.
  */
 function install_reserved_tasks() {
-  return array('configure', 'locale-import', 'locale-batch', 'finished', 'done');
+  return array('configure', 'locale-initial-import', 'locale-initial-batch', 'profile-finished', 'locale-remaining-batch', 'finished', 'done');
 }
 
 /**
@@ -799,11 +851,12 @@ function install_check_requirements($pro
 function install_task_list($active = NULL) {
   // Default list of tasks.
   $tasks = array(
-    'profile-select' => st('Choose profile'),
-    'locale-select'  => st('Choose language'),
-    'requirements'   => st('Verify requirements'),
-    'database'       => st('Setup database'),
-    'configure'      => st('Configure site'),
+    'profile-select'       => st('Choose profile'),
+    'locale-select'        => st('Choose language'),
+    'requirements'         => st('Verify requirements'),
+    'database'             => st('Setup database'),
+    'locale-initial-batch' => st('Setup translations'),
+    'configure'            => st('Configure site'),
   );
 
   $profiles = install_find_profiles();
@@ -826,11 +879,13 @@ function install_task_list($active = NUL
     }
   }
 
-  // If necessary, add translation import to the task list.
-  if (count($locales) > 1 && !empty($_GET['locale']) && $_GET['locale'] != 'en') {
-    $tasks += array(
-      'locale-batch' => st('Import translations'),
-    );
+  if (count($locales) < 2 || empty($_GET['locale']) || $_GET['locale'] == 'en') {
+    // If not required, remove translation import from the task list.
+    unset($tasks['locale-initial-batch']);
+  }
+  else {
+    // If required, add remaining translations import step.
+    $tasks += array('locale-remaining-batch' => st('Finish translations'));
   }
 
   // Add finished step as the last task.
@@ -846,9 +901,10 @@ function install_task_list($active = NUL
   drupal_set_content('left', theme_task_list($tasks, $active));
 }
 
-function install_configure_form() {
-  // This is necessary to add the task to the $_GET args so the install
-  // system will know that it is done and we've taken over.
+/**
+ * Form API array definition for site configuration.
+ */
+function install_configure_form(&$form_state, $url) {
 
   $form['intro'] = array(
     '#value' => st('To configure your website, please provide the following information.'),
@@ -947,6 +1003,7 @@ function install_configure_form() {
     '#value' => st('Save'),
     '#weight' => 15,
   );
+  $form['#action'] = $url;
   $form['#redirect'] = FALSE;
 
   $hook_form_alter = $_GET['profile'] .'_form_alter';
@@ -956,6 +1013,9 @@ function install_configure_form() {
   return $form;
 }
 
+/**
+ * Form API validate fot the site configuration form.
+ */
 function install_configure_form_validate($form, &$form_state) {
   if ($error = user_validate_name($form_state['values']['account']['name'])) {
     form_error($form['admin_account']['account']['name'], $error);
@@ -968,6 +1028,9 @@ function install_configure_form_validate
   }
 }
 
+/**
+ * Form API submit for the site configuration form.
+ */
 function install_configure_form_submit($form, &$form_state) {
   global $user;
 
@@ -1002,7 +1065,10 @@ function install_configure_form_submit($
   // would be required later in the request, so remember it.
   $user->sid = session_id();
 
-  $form_state['redirect'] = 'finished';
+  variable_set('install_configure_finished', TRUE);
 }
 
+/**
+ * Start the installer.
+ */
 install_main();
diff -urp --strip-trailing-cr ../drupal-6.x-dev/profiles/default/default.profile ./profiles/default/default.profile
--- ../drupal-6.x-dev/profiles/default/default.profile	2007-11-06 10:00:31.000000000 +0100
+++ ./profiles/default/default.profile	2007-11-17 18:33:46.000000000 +0100
@@ -5,7 +5,7 @@
  * Return an array of the modules to be enabled when this profile is installed.
  *
  * @return
- *  An array of modules to be enabled.
+ *   An array of modules to enable.
  */
 function default_profile_modules() {
   return array('color', 'comment', 'help', 'menu', 'taxonomy', 'dblog');
@@ -15,7 +15,9 @@ function default_profile_modules() {
  * Return a description of the profile for the initial installation screen.
  *
  * @return
- *   An array with keys 'name' and 'description' describing this profile.
+ *   An array with keys 'name' and 'description' describing this profile,
+ *   and optional 'language' to override the language selection for
+ *   language-specific profiles.
  */
 function default_profile_details() {
   return array(
@@ -39,38 +41,53 @@ function default_profile_task_list() {
 /**
  * Perform any final installation tasks for this profile.
  *
- * The installer goes through the configure -> locale-import ->
- * locale-batch -> finished -> done tasks in this order, if you
- * don't implement this function in your profile.
+ * The installer goes through the profile-select -> locale-select
+ * -> requirements -> database -> locale-initial-batch -> configure
+ * -> locale-remaining-batch -> finished -> done tasks in this order,
+ * if you don't implement this function in your profile.
  *
  * If this function is implemented, you can have any number of
- * custom tasks to perform, implementing a state machine here to
- * walk the user through those tasks, by setting $task to something
- * other then the reserved tasks listed in install_reserved_tasks()
- * and the 'profile' task this function gets called with for first
- * time. If you implement your custom tasks, this function will get called
- * in every HTTP request (for form processing, printing your
- * information screens and so on) until you advance to the
- * 'locale-import' task, with which you hand control back to the
- * installer.
+ * custom tasks to perform after 'configure', implementing a state
+ * machine here to walk the user through those tasks. First time,
+ * this function gets called with $task set to 'profile', and you
+ * can advance to further tasks by setting $task to your tasks'
+ * identifiers, used as array keys in the hook_profile_task_list()
+ * above. You must avoid the reserved tasks listed in
+ * install_reserved_tasks(). If you implement your custom tasks,
+ * this function will get called in every HTTP request (for form
+ * processing, printing your information screens and so on) until
+ * you advance to the 'profile-finished' task, with which you
+ * hand control back to the installer. Each custom page you
+ * return needs to provide a way to continue, such as a form
+ * submission or a link. You should also set custom page titles.
  *
  * You should define the list of custom tasks you implement by
- * returning an array of them in hook_profile_task_list().
+ * returning an array of them in hook_profile_task_list(), as these
+ * show up in the list of tasks on the installer user interface.
  *
- * Should a profile want to display a form here, it can; it should set
- * the task using variable_set('install_task', 'new_task') and use
- * the form technique used in install_tasks() rather than using
- * drupal_get_form().
+ * Remember that the user will be able to reload the pages multiple
+ * times, so you might want to use variable_set() and variable_get()
+ * to remember your data and control further processing, if $task
+ * is insufficient. Should a profile want to display a form here,
+ * it can; the form should set '#redirect' to FALSE, and rely on
+ * an action in the submit handler, such as variable_set(), to
+ * detect submission and proceed to further tasks.
+ *
+ * Important: Any temporary variables should be removed using
+ * variable_del() before advancing to the 'profile-finished' phase.
  *
  * @param $task
  *   The current $task of the install system. When hook_profile_tasks()
  *   is first called, this is 'profile'.
+ * @param $url
+ *   Complete URL to be used for a link or form action on a custom page,
+ *   if providing any, to allow the user to proceed with the installation.
  *
  * @return
  *   An optional HTML string to display to the user. Only used if you
  *   modify the $task, otherwise discarded.
  */
-function default_profile_tasks(&$task) {
+function default_profile_tasks(&$task, $url) {
 
   // Insert default user-defined node types into the database. For a complete
   // list of available node type attributes, refer to the node type API
