The menu module allows to translate menu items, but not the link, so if i have aliases defined for the translated nodes, only one will show up, the original one. I modified two functions to allow the links to be translated and get the right alias for the current language. This works with menu items and also with the language block. Not tested with other links.

To apply this patch, replace the functions i18n_path and i18n_url_rewrite in the file i18n.inc with this ones:

function i18n_url_rewrite( $type, $path, $original ) {
  //drupal_set_message("type=$type path=$path original=$original");
  $lang = i18n_get_lang();
  // beware, despite the type they are not always alias
  if ( $type == 'alias' ) {
    // always try to translate the alias to find the translated one
    if ( i18n_get_lang_prefix($path) && $path == $original ) {
      // paths already translated seems to be equals to the original, such as those from languages block
      return $path;
    } else {
      return i18n_path( $original, $lang );
    }
  } else {
    // always translate source paths
    return i18n_path( $path, $lang );
  }
}

function i18n_path($path, $lang) {
  if (!$path || $path == i18n_frontpage($lang)) {
    return $lang;
  } else {
    $explodedpath = explode( '/', $path );
    // if path is for a node, find the translation and its alias
    if ( count( $explodedpath ) == 2 && $explodedpath[0] == 'node' ) {
      $sql = 'SELECT b.nid FROM {i18n_node} a INNER JOIN {i18n_node} b ON a.trid = b.trid WHERE a.nid = %d AND b.language = \'%s\'';
      $nid = db_result( db_query( $sql, $explodedpath[1], $lang ) );
      if ( ! $nid ) {
        // if not translated, treat as if it is not a node path
        return $lang . '/' . $path;
      } else {
        // update path with translated node and process normally
        $path = 'node/' . $nid;
  		
        if ( $alias = drupal_lookup_path( 'alias', $path ) ) {
          if( $prefix = i18n_get_lang_prefix( $alias ) ) {
            // note that the prefix cannot be invalid
            return $alias;
          } else {
            return $lang . '/' . $alias;
          }
        } else {
          return $lang . '/' . $path;
        }
      }
    } else {
      // default behaviour for paths
      return $lang . '/' . $path;
    }
  }
}

I hope this is usefull :)

Comments

Bodo Maass’s picture

This patch doesn't work properly with untranslated items. If you have more than one untranslated items, the SQL query will always create a match. To prevent that, append 'AND a.trid != 0' to the query.

Further, when a translation does not exist, we can still use a friendly path. For example, the current path is en/about and we want the link for de/about, which doesn't exist yet. Instead of returning something like node/23, we can return de/about, which will still work.
Here is my new version of the function i18n_path:

function i18n_path($path, $lang) {
  if (!$path || $path == i18n_frontpage($lang)) {
    return $lang;
  } else {
    $explodedpath = explode( '/', $path );
    $result = $lang . '/' . $path;
    // if path is for a node, find the translation and its alias
    if ( count( $explodedpath ) == 2 && $explodedpath[0] == 'node' ) {
      $sql = 'SELECT b.nid FROM {i18n_node} a INNER JOIN {i18n_node} b ON a.trid = b.trid WHERE a.nid = %d AND b.language = \'%s\' AND a.trid != 0';
      $nid = db_result( db_query( $sql, $explodedpath[1], $lang ) );
      if ( $nid ) {
        // update path with translated node
        $path = 'node/' . $nid;
      }
      if ( $alias = drupal_lookup_path( 'alias', $path ) ) {
        if( $prefix = i18n_get_lang_prefix( $alias ) ) {
          // note that the prefix cannot be invalid
          return $alias;
        } else {
          return $lang . '/' . $alias;
        }
      }
    }
    return $result;
  }
}
Bodo Maass’s picture

There is another problem with this, and that is the admin/node page. If you have groups of translated nodes, they will all have the same alias on the admin page, since all their links will be translated into the current language.
That is not a showstopper, since the "translate links" feature is just doing its job, and it may be hard to turn this off without some hacking.
A possible workaround might be to filter the node list by language, so that only nodes for the current language are listed, as in the menus.

ethdra’s picture

Using friendly urls when no translation exists only works when url aliases has no language prefix, so use with care. However, i still prefer showing node/25-like urls when no translation available.
A workaround for the content administration problem is to remove the re-translation of the url aliases in the url_rewrite function. Here is the code for the i18n_url_rewrite function.

function i18n_url_rewrite( $type, $path, $original ) {
  //drupal_set_message("type=$type path=$path original=$original");
  $lang = i18n_get_lang();
  // beware, despite the type they are not always alias
  if ( $type == 'alias' ) {
    // always try to translate the alias to find the translated one
    if ( i18n_get_lang_prefix($path) && $path == $original ) {
      // paths already translated seems to be equals to the original, such as those from languages block
      return $path;
    } else {
    	return $lang .'/'.$path;
    }
  } else {
    // always translate source paths
    return i18n_path( $path, $lang );
  }
}

Thx for the suggestions and the patch :)

Bodo Maass’s picture

This patch no longer seems to work with 4.7.3 and the latest i18n from 5 August 2006. ethdra, did you already update it to the latest version? I'm just playing around with it. How can we gather support for this patch to become official?

Bodo Maass’s picture

Hi ethdra,
I played around a bit more with that. It does work with 4.7.3 after all. Your url_rewrite function from July 3 does not work properly, however. It fixes the admin page, but if there are links that already contain a language prefix, it breaks. In particular, if the current language is english and it is asked to rewrite de/mypage it will return de/de/mypage. The original version does not do this, so it is better.

About the i18n_path function: I guess it is a matter of preference how you want to treat untranslated nodes. However, with your version aliases only work if the node has at least one translation. For example, if I create a new page in english with the title 'About' and no translation, path_auto will create the alias 'about' for this page. But the english menu will instead show the raw node path (like 'node/37') until there is at least one translation for it.
My suggestion would be to point to the correct alias if it exists, and to point to node/37 for those languages where no translation exists yet. What do you think about that?

ethdra’s picture

The problem with the last i18n release, was that i18n_path funcion was no longer in i18n.inc. I've fixed de i18n_url_rewrite func, hope it works now. I guess you were right about using aliases when no translation is available, fixed that too.
So, the patch process is to replace i18n_url_rewrite function in i18n.inc file, and i18n_path function in i18n.module file. This is the new i18n_url_rewrite function:

function i18n_url_rewrite( $type, $path, $original ) {
  //drupal_set_message("type=$type path=$path original=$original");
  $lang = i18n_get_lang();
  // beware, despite the type they are not always alias
  if ( $type == 'alias' ) {
    // always try to translate the alias to find the translated one
    if ( i18n_get_lang_prefix($path) ) {
      return $path;
    } else {
      return $lang .'/'.$path;
    }
  } else {
    // always translate source paths
    return i18n_path( $path, $lang );
  }
}

And this is the new i18n_path function:

function i18n_path($path, $lang) {
  if (!$path || $path == i18n_frontpage($lang)) {
    return $lang;
  } else {
    $explodedpath = explode( '/', $path );
    // if path is for a node, find the translation and its alias
    if ( count( $explodedpath ) == 2 && $explodedpath[0] == 'node' ) {
      $sql = 'SELECT b.nid FROM {i18n_node} a INNER JOIN {i18n_node} b ON a.trid = b.trid WHERE a.nid = %d AND b.language = \'%s\'';
      $nid = db_result( db_query( $sql, $explodedpath[1], $lang ) );
      if ( $nid ) {
        // update path with translated node and process normally
        $path = 'node/' . $nid;
      }
      if ( $alias = drupal_lookup_path( 'alias', $path ) ) {
        if( $prefix = i18n_get_lang_prefix( $alias ) ) {
          if ( $prefix == $lang ) {
	          return $alias;
	        } else {
	          return $lang . '/' . $path;
	        }
        } else {
          return $lang . '/' . $alias;
        }
      } else {
        return $lang . '/' . $path;
      }
    } else {
      // default behaviour for paths
      return $lang . '/' . $path;
    }
  }
}

Again, thx for your suggestions and testing, i've been quite busy, and not thinking in drupal too much, so sorry for the late.
I dont know how to get this in the official code, i just hope one day the author will see this path and apply it, meanwhile, we can only call his attention to this patch by mail or similar.

natuk’s picture

I hope this is not something obvious but I applied the patch for the function i18n_url_rewrite and although the menus worked great, for some reason the slogan of the website disappeared alongside the custom nodes for "page not found". When I reverted to the original function these problems no longer existed and the menu still worked, although I haven't tried it with menu items created after reverting.

natuk’s picture

Rushed here, the menu does not work after reverting... Not sure what is happening.

jose reyero’s picture

Status: Needs review » Closed (won't fix)

Per language menu items are implemented in next version, for Drupal 5 so translation is not needed anymore for menu items.

I won't be backporting new features to 4.7. But is someone feels like it please post a patch -actual patch- and reopen.