Index: includes/bootstrap.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v
retrieving revision 1.269
diff -u -p -r1.269 bootstrap.inc
--- includes/bootstrap.inc	31 Jan 2009 16:50:56 -0000	1.269
+++ includes/bootstrap.inc	3 Feb 2009 12:58:02 -0000
@@ -130,15 +130,15 @@ define('DRUPAL_BOOTSTRAP_SESSION', 4);
 define('DRUPAL_BOOTSTRAP_VARIABLES', 5);
 
 /**
- * Seventh bootstrap phase: load bootstrap.inc and module.inc, start
- * the variable system and try to serve a page from the cache.
+ * Seventh bootstrap phase: find out language of the page.
  */
-define('DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE', 6);
+define('DRUPAL_BOOTSTRAP_LANGUAGE', 6);
 
 /**
- * Eighth bootstrap phase: find out language of the page.
+ * Eigth bootstrap phase: load bootstrap.inc and module.inc, start
+ * the variable system and try to serve a page from the cache.
  */
-define('DRUPAL_BOOTSTRAP_LANGUAGE', 7);
+define('DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE', 7);
 
 /**
  * Nineth bootstrap phase: set $_GET['q'] to Drupal path of request.
@@ -1086,14 +1086,14 @@ function drupal_anonymous_user($session 
  *     DRUPAL_BOOTSTRAP_ACCESS: identify and reject banned hosts.
  *     DRUPAL_BOOTSTRAP_SESSION: initialize session handling.
  *     DRUPAL_BOOTSTRAP_VARIABLES: initialize variable handling.
+ *     DRUPAL_BOOTSTRAP_LANGUAGE: identify the language used on the page.
  *     DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE: load bootstrap.inc and module.inc, start
  *       the variable system and try to serve a page from the cache.
- *     DRUPAL_BOOTSTRAP_LANGUAGE: identify the language used on the page.
  *     DRUPAL_BOOTSTRAP_PATH: set $_GET['q'] to Drupal path of request.
  *     DRUPAL_BOOTSTRAP_FULL: Drupal is fully loaded, validate and fix input data.
  */
 function drupal_bootstrap($phase = NULL) {
-  static $phases = array(DRUPAL_BOOTSTRAP_CONFIGURATION, DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE, DRUPAL_BOOTSTRAP_DATABASE, DRUPAL_BOOTSTRAP_ACCESS, DRUPAL_BOOTSTRAP_SESSION, DRUPAL_BOOTSTRAP_VARIABLES, DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE, DRUPAL_BOOTSTRAP_LANGUAGE, DRUPAL_BOOTSTRAP_PATH, DRUPAL_BOOTSTRAP_FULL), $completed_phase = -1;
+  static $phases = array(DRUPAL_BOOTSTRAP_CONFIGURATION, DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE, DRUPAL_BOOTSTRAP_DATABASE, DRUPAL_BOOTSTRAP_ACCESS, DRUPAL_BOOTSTRAP_SESSION, DRUPAL_BOOTSTRAP_VARIABLES, DRUPAL_BOOTSTRAP_LANGUAGE, DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE, DRUPAL_BOOTSTRAP_PATH, DRUPAL_BOOTSTRAP_FULL), $completed_phase = -1;
 
   if (isset($phase)) {
     while ($phases && $phase > $completed_phase) {
@@ -1117,7 +1117,7 @@ function drupal_get_bootstrap_phase() {
 }
 
 function _drupal_bootstrap($phase) {
-  global $conf, $user;
+  global $conf, $user, $no_cache;
 
   switch ($phase) {
 
@@ -1180,10 +1180,14 @@ function _drupal_bootstrap($phase) {
       $conf = variable_init(isset($conf) ? $conf : array());
       break;
 
+    case DRUPAL_BOOTSTRAP_LANGUAGE:
+      drupal_init_language();
+      break;
+
     case DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE:
       $cache_mode = variable_get('cache', CACHE_DISABLED);
       // Get the page from the cache.
-      $cache = $cache_mode == CACHE_DISABLED ? FALSE : page_get_cache(TRUE);
+      $cache = $cache_mode == CACHE_DISABLED || $no_cache == TRUE ? FALSE : page_get_cache(TRUE);
       // If the skipping of the bootstrap hooks is not enforced, call hook_boot.
       if (!is_object($cache) || $cache_mode != CACHE_AGGRESSIVE) {
         // Load module handling.
@@ -1215,10 +1219,6 @@ function _drupal_bootstrap($phase) {
       }
       break;
 
-    case DRUPAL_BOOTSTRAP_LANGUAGE:
-      drupal_init_language();
-      break;
-
     case DRUPAL_BOOTSTRAP_PATH:
       require_once DRUPAL_ROOT . '/includes/path.inc';
       // Initialize $_GET['q'] prior to loading modules and invoking hook_init().
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.861
diff -u -p -r1.861 common.inc
--- includes/common.inc	2 Feb 2009 15:42:43 -0000	1.861
+++ includes/common.inc	3 Feb 2009 12:58:04 -0000
@@ -1846,7 +1846,7 @@ function l($text, $path, array $options 
  * react to the closing of the page by calling hook_exit().
  */
 function drupal_page_footer() {
-  global $user;
+  global $user, $no_cache;
 
   // Destroy empty anonymous sessions if possible.
   if (!headers_sent() && drupal_session_is_started() && empty($_SESSION) && !$user->uid) {
@@ -1856,7 +1856,7 @@ function drupal_page_footer() {
     watchdog('session', '$_SESSION is non-empty yet no code has called drupal_session_start().', array(), WATCHDOG_NOTICE);
   }
 
-  if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) {
+  if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED && $no_cache != TRUE) {
     page_set_cache();
   }
 
Index: includes/language.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/language.inc,v
retrieving revision 1.19
diff -u -p -r1.19 language.inc
--- includes/language.inc	1 Feb 2009 16:45:53 -0000	1.19
+++ includes/language.inc	3 Feb 2009 12:58:04 -0000
@@ -10,7 +10,7 @@
  *  Choose a language for the page, based on language negotiation settings.
  */
 function language_initialize() {
-  global $user;
+  global $user, $no_cache;
 
   // Configured presentation language mode.
   $mode = variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE);
@@ -59,6 +59,11 @@ function language_initialize() {
 
   // Browser accept-language parsing.
   if ($language = language_from_browser()) {
+    // If the language is set from  browser preferences, set $no_cache to TRUE
+    // to avoid caching the page in this language for subsequent requests.
+    if ($language != language_default()) {
+      $no_cache = TRUE;
+    }
     return $language;
   }
 
Index: modules/simpletest/tests/bootstrap.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/bootstrap.test,v
retrieving revision 1.12
diff -u -p -r1.12 bootstrap.test
--- modules/simpletest/tests/bootstrap.test	31 Jan 2009 16:50:57 -0000	1.12
+++ modules/simpletest/tests/bootstrap.test	3 Feb 2009 12:58:05 -0000
@@ -80,6 +80,83 @@ class BootstrapIPAddressTestCase extends
   }
 }
 
+/**
+ * Test page caching with language negotiation.
+ */
+class BootstrapPageCacheWithLanguageFallbackTestCase extends DrupalWebTestCase {
+
+  function getInfo() {
+    return array(
+      'name' => t('Page cache test with language negotiation'),
+      'description' => t('Enable the page cache and language negotiation, ensure that cached pages are served in the correct language.'),
+      'group' => t('Bootstrap')
+    );
+  }
+
+  function setUp() {
+    parent::setUp('locale');
+    $this->web_user = $this->drupalCreateUser(array('administer languages', 'access administration pages'));
+    $this->drupalLogin($this->web_user);
+
+    // Enable French language.
+    $edit = array();
+    $edit['langcode'] = 'fr';
+
+    $this->drupalPost('admin/settings/language/add', $edit, t('Add language'));
+    $this->drupalLogout($this->web_user);
+
+    // Set language negotiation to "Path prefix with fallback".
+    variable_set('language_negotiation', LANGUAGE_NEGOTIATION_PATH);
+
+    // Force inclusion of language.inc.
+    drupal_init_language();
+    // Enable the page cache.
+    variable_set('cache', CACHE_NORMAL);
+  }
+
+  /**
+   * Test page caching with language fallback.
+   */
+  function testPageCacheLanguageFallback() {
+
+    // Visit the root path with browser language preference set to French.
+    // Since this is in the default site language (English), the page should
+    // not be cached for this request.
+    $this->drupalGet('', array(), array('Accept-Language: fr'));
+
+    // The visitor should be served the page in their preferred language.
+    $this->assertRaw('xml:lang="fr"', t('Page returned in the correct language.'));
+
+    // Since the page has been set by language preferences, we should neither
+    // serve the page from cache nor cache the rendered page..
+    $this->assertFalse(cache_get(url('', array('absolute' => TRUE)), 'cache_page'), t('Page has not been cached.'));
+    $this->assertFalse($this->drupalGetHeader('Etag'), t('Page not served from cache.'));
+
+    // Visit the page again with no browser language preference, and confirm
+    // that it is returned in the correct language.
+    $this->drupalGet('');
+    $this->assertRaw('xml:lang="en"', t('Page returned in the correct language.'));
+    // This request should not have been served from cache since there have
+    // been no valid requests.
+    $this->assertFalse($this->drupalGetHeader('Etag'), t('Page not served from cache.'));
+    // Confirm the cache was populated from this request.
+    $this->assertTrue(cache_get(url('', array('absolute' => TRUE)), 'cache_page'), t('Page has been cached.'));
+
+    // Ensure that the page served from cache correctly when requested again.
+    $this->drupalGet('');
+    $this->assertTrue($this->drupalGetHeader('Etag'), t('Page served from cache'));
+    $this->assertRaw('xml:lang="en"', t('Page returned in the correct language.'));
+
+    // Now that the page has been cached, visit again with the browser set to
+    // accept French, and confirm the page is not served from cache.
+    $this->drupalGet('', array(), array('Accept-Language: fr'));
+
+    // The visitor should be served the page in their preferred language.
+    $this->assertRaw('xml:lang="fr"', t('Page returned in the correct language.'));
+    $this->assertFalse($this->drupalGetHeader('Etag'), t('Page not served from cache.'));
+  }
+}
+
 class BootstrapPageCacheTestCase extends DrupalWebTestCase {
 
   function getInfo() {
