Index: includes/locale.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/locale.inc,v
retrieving revision 1.261
diff -u -p -r1.261 locale.inc
--- includes/locale.inc	26 Oct 2010 15:24:50 -0000	1.261
+++ includes/locale.inc	2 Nov 2010 11:08:33 -0000
@@ -23,6 +23,12 @@ define('LOCALE_LANGUAGE_NEGOTIATION_BROW
 define('LOCALE_LANGUAGE_NEGOTIATION_INTERFACE', 'locale-interface');
 
 /**
+ * The language is determined using an already detected one if no URL language
+ * is available.
+ */
+define('LOCALE_LANGUAGE_NEGOTIATION_URL_FALLBACK', 'locale-url-fallback');
+
+/**
  * The language is set based on the user language settings.
  */
 define('LOCALE_LANGUAGE_NEGOTIATION_USER', 'locale-user');
@@ -78,6 +84,28 @@ function locale_language_from_interface(
 }
 
 /**
+ * Identifies the language to be assigned to URLs when none is detected.
+ *
+ * If no URL language can be detected and the default language is configured to
+ * always provide language information via URL prefix or domain, we need to fall
+ * back to the most suitable language, which is an already detected language.
+ *
+ * @param $languages
+ *   Optional. An array of valid language objects. Defaults to NULL.
+ * @param $language_type
+ *   Optional. The language type to fall back to. Defaults to the interface
+ *   language.
+ *
+ * @return
+ *   A valid language code on success, FALSE otherwise.
+ */
+function locale_language_from_url_fallback($language = NULL, $language_type = LANGUAGE_TYPE_INTERFACE) {
+  $default = language_default();
+  $prefix = variable_get('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX) == LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX;
+  return ($prefix && empty($default->prefix)) || (!$prefix && empty($default->domain)) ? FALSE : $GLOBALS[$language_type]->language;
+}
+
+/**
  * Identify language from the Accept-language HTTP header we got.
  *
  * We perform browser accept-language parsing only if page cache is disabled,
Index: modules/locale/locale.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.install,v
retrieving revision 1.69
diff -u -p -r1.69 locale.install
--- modules/locale/locale.install	20 Oct 2010 00:37:20 -0000	1.69
+++ modules/locale/locale.install	2 Nov 2010 09:06:58 -0000
@@ -113,6 +113,18 @@ function locale_update_7001() {
 }
 
 /**
+ * Updates URL language negotiation by adding the URL fallback detection method.
+ */
+function locale_update_7002() {
+  $language_types_info = language_types_info();
+  $info = $language_types_info[LANGUAGE_TYPE_URL];
+  if (isset($info['fixed'])) {
+    language_negotiation_set(LANGUAGE_TYPE_URL, array_flip($info['fixed']));
+  }
+  return array();
+}
+
+/**
  * @} End of "defgroup updates-6.x-to-7.x"
  */
 
Index: modules/locale/locale.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v
retrieving revision 1.304
diff -u -p -r1.304 locale.module
--- modules/locale/locale.module	25 Oct 2010 15:14:33 -0000	1.304
+++ modules/locale/locale.module	2 Nov 2010 08:51:44 -0000
@@ -503,8 +503,8 @@ function locale_entity_info_alter(&$enti
  *   language negotiated value. It is used by the Field API to determine the
  *   display language for fields if no explicit value is specified.
  * - URL language is by default non-configurable and is determined through the
- *   URL language provider. It is used by l() as the default language if none is
- *   specified.
+ *   URL language provider or the URL fallback provider if no language can be
+ *   detected. It is used by l() as the default language if none is specified.
  */
 function locale_language_types_info() {
   require_once DRUPAL_ROOT . '/includes/locale.inc';
@@ -517,7 +517,7 @@ function locale_language_types_info() {
       'fixed' => array(LOCALE_LANGUAGE_NEGOTIATION_INTERFACE),
     ),
     LANGUAGE_TYPE_URL => array(
-      'fixed' => array(LOCALE_LANGUAGE_NEGOTIATION_URL),
+      'fixed' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LOCALE_LANGUAGE_NEGOTIATION_URL_FALLBACK),
     ),
   );
 }
@@ -582,6 +582,15 @@ function locale_language_negotiation_inf
     'description' => t('Use the detected interface language.'),
   );
 
+  $providers[LOCALE_LANGUAGE_NEGOTIATION_URL_FALLBACK] = array(
+    'types' => array(LANGUAGE_TYPE_URL),
+    'callbacks' => array('language' => 'locale_language_from_url_fallback'),
+    'file' => $file,
+    'weight' => 8,
+    'name' => t('URL Fallback'),
+    'description' => t('Return a suitable language for URLs if none is detected.'),
+  );
+
   return $providers;
 }
 
Index: modules/locale/locale.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.test,v
retrieving revision 1.83
diff -u -p -r1.83 locale.test
--- modules/locale/locale.test	26 Oct 2010 15:24:50 -0000	1.83
+++ modules/locale/locale.test	2 Nov 2010 11:10:27 -0000
@@ -1730,15 +1730,14 @@ class LocaleUILanguageNegotiationTest ex
     parent::setUp('locale', 'locale_test');
     require_once DRUPAL_ROOT . '/includes/language.inc';
     drupal_load('module', 'locale');
+    $admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages', 'administer blocks'));
+    $this->drupalLogin($admin_user);
   }
 
   /**
    * Tests for language switching by URL path.
    */
   function testUILanguageNegotiation() {
-    $admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages'));
-    $this->drupalLogin($admin_user);
-
     // A few languages to switch to.
     // This one is unknown, should get the default lang version.
     $language_unknown = 'blah-blah';
@@ -1892,6 +1891,54 @@ class LocaleUILanguageNegotiationTest ex
     $this->drupalGet($test['path'], array(), $test['http_header']);
     $this->assertText($test['expect'], $test['message']);
   }
+
+  /**
+   * Test URL language detection when the requested URL has no language.
+   */
+  function testUrlLanguageFallback() {
+    drupal_static_reset('language_list');
+
+    // Add the Italian language.
+    $language_browser_fallback = 'it';
+    locale_add_language($language_browser_fallback);
+    $languages = language_list();
+
+    // Enable the path prefix for the default language: this way any unprefixed
+    // URL must have a valid fallback value.
+    $edit = array('prefix' => 'en');
+    $this->drupalPost('admin/config/regional/language/edit/en', $edit, t('Save language'));
+
+    // Enable browser and URL language detection.
+    $edit = array(
+      'language[enabled][locale-browser]' => TRUE,
+      'language[enabled][locale-url]' => TRUE,
+      'language[weight][locale-browser]' => -8,
+      'language[weight][locale-url]' => -10,
+    );
+    $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
+    $this->drupalGet('admin/config/regional/language/configure');
+
+    // Enable the language switcher block.
+    $edit = array('blocks[locale_language][region]' => 'sidebar_first');
+    $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
+
+    // Access the front page without specifying any valid URL language prefix
+    // and having as browser language preference a non-default language.
+    $http_header = array("Accept-Language: $language_browser_fallback;q=1");
+    $this->drupalGet('', array(), $http_header);
+
+    // Check that the language switcher active link matches the given browser
+    // language.
+    $args = array(':url' => "/$language_browser_fallback");
+    $xpath = '//div[@id="block-locale-language"]//a[@class="language-link active" and @href=:url]';
+    $fields = $this->xpath($xpath, $args);
+    $this->assertTrue($fields[0] == $languages[$language_browser_fallback]->native, t('The browser language is the URL active language'));
+
+    // Check that URLs are rewritten using the given browser language.
+    $xpath = '//div[@id="site-name"]//a[@rel="home" and @href=:url]//span';
+    $fields = $this->xpath($xpath, $args);
+    $this->assertTrue($fields[0] == 'Drupal', t('URLs are rewritten using the browser language.'));
+  }
 }
 
 /**
