Index: modules/locale/locale.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v
retrieving revision 1.297
diff -u -p -r1.297 locale.module
--- modules/locale/locale.module	5 Aug 2010 08:08:43 -0000	1.297
+++ modules/locale/locale.module	29 Aug 2010 16:50:11 -0000
@@ -962,7 +962,11 @@ function locale_block_view($type) {
 function locale_url_outbound_alter(&$path, &$options, $original_path) {
   // Only modify internal URLs.
   if (!$options['external'] && drupal_multilingual()) {
-    static $callbacks;
+    static $drupal_static_fast;
+    if (!isset($drupal_static_fast)) {
+      $drupal_static_fast['callbacks'] = &drupal_static(__FUNCTION__);
+    }
+    $callbacks = &$drupal_static_fast['callbacks'];
 
     if (!isset($callbacks)) {
       $callbacks = array();
@@ -990,6 +994,11 @@ function locale_url_outbound_alter(&$pat
     foreach ($callbacks as $callback) {
       $callback($path, $options);
     }
+
+    // No language dependent path allowed in this mode.
+    if (empty($callbacks)) {
+      unset($options['language']);
+    }
   }
 }
 
Index: modules/path/path.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/path/path.test,v
retrieving revision 1.40
diff -u -p -r1.40 path.test
--- modules/path/path.test	5 Aug 2010 23:53:38 -0000	1.40
+++ modules/path/path.test	29 Aug 2010 16:50:11 -0000
@@ -299,6 +299,7 @@ class PathLanguageTestCase extends Drupa
 
     // Confirm that the alias is returned by url().
     drupal_static_reset('language_list');
+    drupal_static_reset('locale_url_outbound_alter');
     $languages = language_list();
     $url = url('node/' . $french_node->nid, array('language' => $languages[$french_node->language]));
     $this->assertTrue(strpos($url, $edit['path[alias]']), t('URL contains the path alias.'));
Index: modules/translation/translation.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/translation/translation.module,v
retrieving revision 1.83
diff -u -p -r1.83 translation.module
--- modules/translation/translation.module	30 Aug 2010 05:58:46 -0000	1.83
+++ modules/translation/translation.module	30 Aug 2010 08:42:29 -0000
@@ -177,19 +177,56 @@ function translation_form_alter(&$form, 
 /**
  * Implements hook_node_view().
  *
- * Display translation links with native language names, if this node
- * is part of a translation set.
+ * Display translation links with native language names, if this node is part of
+ * a translation set. If no language provider is enabled "fall back" to the
+ * simple links built through the result of translation_node_get_translations().
  */
 function translation_node_view($node, $view_mode) {
+  // If the site has no translations or is not multilingual we have no content
+  // translation links to display.
   if (isset($node->tnid) && drupal_multilingual() && $translations = translation_node_get_translations($node->tnid)) {
-    $path = 'node/' . $node->nid;
-    $links = language_negotiation_get_switch_links(LANGUAGE_TYPE_INTERFACE, $path);
-    if (is_object($links)) {
-      $links = $links->links;
-      // Do not show link to the same node.
-      unset($links[$node->language]);
-      $node->content['links']['#links'] = array_merge($node->content['links']['#links'], $links);
+    $languages = language_list('enabled');
+    $languages = $languages[1];
+
+    // There might be a language provider enabled defining custom language
+    // switch links which need to be taken into account while generating the
+    // content translation links. As custom language switch links are available
+    // only for configurable language types and interface language is the only
+    // configurable language type in core, we use it as default. Contributed
+    // modules can change this behavior by setting the system variable below.
+    $type = variable_get('translation_language_type', LANGUAGE_TYPE_INTERFACE);
+    $custom_links = language_negotiation_get_switch_links($type, "node/$node->nid");
+    $links = array();
+
+    foreach ($translations as $langcode => $translation) {
+      // Do not show link to the same node or to translations in disabled
+      // languages.
+      if (isset($languages[$langcode]) && $langcode != $node->language) {
+        $language = $languages[$langcode];
+        $key = "translation_$langcode";
+
+        if (isset($custom_links->links[$langcode])) {
+          $links[$key] = $custom_links->links[$langcode];
+        }
+        else {
+          $links[$key] = array(
+            'href' => "node/$translation->nid",
+            'title' => $language->native,
+            'language' => $language,
+          );
+        }
+
+        // Custom switch links are more generic than content translation links,
+        // hence we override existing attributes with the ones below.
+        if (!isset($links[$key]['attributes'])) {
+          $links[$key]['attributes'] = array();
+        }
+        $attributes = array('title' => $translation->title, 'class' => array('translation-link'));
+        $links[$key]['attributes'] = array_merge($links[$key]['attributes'], $attributes);
+      }
     }
+
+    $node->content['links']['#links'] = array_merge($node->content['links']['#links'], $links);
   }
 }
 
@@ -434,7 +471,8 @@ function translation_path_get_translatio
  * Replaces links with pointers to translated versions of the content.
  */
 function translation_language_switch_links_alter(array &$links, $type, $path) {
-  if ($type == LANGUAGE_TYPE_INTERFACE && $paths = translation_path_get_translations($path)) {
+  $language_type = variable_get('translation_language_type', LANGUAGE_TYPE_INTERFACE);
+  if ($type == $language_type && $paths = translation_path_get_translations($path)) {
     foreach ($links as $langcode => $link) {
       if (isset($paths[$langcode])) {
         // Translation in a different node.
Index: modules/translation/translation.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/translation/translation.test,v
retrieving revision 1.29
diff -u -p -r1.29 translation.test
--- modules/translation/translation.test	5 Aug 2010 23:53:39 -0000	1.29
+++ modules/translation/translation.test	29 Aug 2010 16:50:12 -0000
@@ -14,12 +14,7 @@ class TranslationTestCase extends Drupal
 
   function setUp() {
     parent::setUp('locale', 'translation');
-  }
 
-  /**
-   * Create a basic page with translation, modify the basic page outdating translation, and update translation.
-   */
-  function testContentTranslation() {
     // Setup users.
     $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages'));
     $translator = $this->drupalCreateUser(array('create page content', 'edit own page content', 'translate content'));
@@ -39,7 +34,13 @@ class TranslationTestCase extends Drupal
 
     $this->drupalLogout();
     $this->drupalLogin($translator);
+  }
 
+  /**
+   * Create a basic page with translation, modify the basic page outdating
+   * translation, and update translation.
+   */
+  function testContentTranslation() {
     // Create Basic page in English.
     $node_title = $this->randomName();
     $node_body =  $this->randomName();
@@ -86,6 +87,32 @@ class TranslationTestCase extends Drupal
   }
 
   /**
+   * Check that content translation links behave properly.
+   */
+  function testContentTranslationLinks() {
+    // Create Basic page in English.
+    $node_title = $this->randomName();
+    $node_body = $this->randomName();
+    $node = $this->createPage($node_title, $node_body, 'en');
+
+    // Submit translation in Spanish.
+    $node_translation_title = $this->randomName();
+    $node_translation_body = $this->randomName();
+    $node_translation = $this->createTranslation($node, $node_translation_title, $node_translation_body, 'es');
+
+    // Check that content translation links are shown even when no language
+    // negotiation is configured.
+    $languages = language_list();
+    $this->drupalGet("node/$node->nid");
+    $url = url("node/$node_translation->nid");
+    $this->assertContentByXPath('//a[@href=:url]', array(':url' => $url), $languages['es']->native, t('Spanish translation link found.'));
+
+    $this->drupalGet("node/$node_translation->nid");
+    $url = url("node/$node->nid");
+    $this->assertContentByXPath('//a[@href=:url]', array(':url' => $url), $languages['en']->native, t('English translation link found.'));
+  }
+
+  /**
    * Install a the specified language if it has not been already. Otherwise make sure that
    * the language is enabled.
    *
@@ -172,4 +199,40 @@ class TranslationTestCase extends Drupal
 
     return $node;
   }
+
+  /**
+   * Assert that an element identified by the given XPath has the given content.
+   *
+   * @param $xpath
+   *   XPath used to find the element.
+   * @param array $arguments
+   *   An array of arguments with keys in the form ':name' matching the
+   *   placeholders in the query. The values may be either strings or numeric
+   *   values.
+   * @param $value
+   *   The text content of the matched element to assert.
+   * @param $message
+   *   Message to display.
+   * @param $group
+   *   The group this message belongs to.
+   *
+   * @return
+   *   TRUE on pass, FALSE on fail.
+   */
+  function assertContentByXPath($xpath, array $arguments = array(), $value = NULL, $message = '', $group = 'Other') {
+    $elements = $this->xpath($xpath, $arguments);
+
+    $found = TRUE;
+    if ($value && $elements) {
+      $found = FALSE;
+      foreach ($elements as $element) {
+        if ((string) $element == $value) {
+          $found = TRUE;
+          break;
+        }
+      }
+    }
+
+    return $this->assertTrue($elements && $found, $message, $group);
+  }
 }
