intlinks is exactly what I search for my site.

But it doesn't seem to work in a multilingual settings environement.
I use base_url/en/node/nid urls.
When I write

base_url/node/nid

The link is ok but the path don't include the langage variable. (404)

When I write

base_url/en/node/nid

The link is not converted with the alias.

Can you take in charge the syntax with language variable :

/language/node/nid

Thanks !

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

LoMo’s picture

Could you please explain your use case in a bit more detail? Are you linking from one node to other nodes that are translations (in another language?), and therefore linking with a language code in the path?

I'll have to examine this a bit more. The site I created this module for was a single-language site, but I've done a fair bit with multilingual sites now and it can be a bit tricky, especially since the "Drupal 7 way" is still evolving and wasn't quite worked out at the time I wrote the module. I'm not sure if you are translating (localizing) each field, but not using a new node as the translation of all the fields together (e.g. how it was more typically done in Drupal 6), or whether the translated versions share the same node ID. I've also, more typically, had multilanguage sites where different domains were used for different languages, rather than language codes. So I'll have to experiment and make sure I can get a multilingual site to work with either method.

Thanks for helping me work out the "to-do list" -- and please do follow up with more details of your use case as I'm sure I know how to exactly replicate the problems you are having. It might be time for this module to get some "configuration" options, so if you have particular suggestions, now might be a good time.

Cheers,

Lowell

mentat.fr’s picture

LoMo many thanks for your reply and offer.
Here some details of my configuration and sorry for bad english & explanations.

I use the Internationalization module with Multilingual content, clean url, path translation, pathauto.
I have multiple languages settings in :

Home » Administration » Configuration » Regional and language

All languages have a Path prefix language code

English "en"
French "fr" ...

So all url have path prefixes before the clean url setting (with pathauto module).

http://www.domain/en/

go to english homepage

http://www.domain/fr/

go to french homepage

In content type settings there is a tab to synchronize fields betwen all translations of a node. So I select wich of Standard node fields and Configurable fields (cck in core of D7) are same between translations.

When I create a node is localize in the default language.
After saving it a path in the default language is created with path autospecification :
ex: a sub /content/ for articles and the title of the node :

http://www.domain/en/content/title-of-the-node-in-english

If I want to localize, I edit this node, go to the translate tab and select "add translation" in the appropriate language.
Drupal create a new node who is link to the node as a translation in the language.

The new translate node has a different "nid" with some fields translate and some commons (synchronize).
A new url is compose with the language code and path auto specification:

http://www.domain/fr/contenu/titre-du-noeud-en-francais

When user browse the url :

http://www.domain/en/content/title-of-the-node-in-english

I have made a language selector for switching between translation so if he click on french he go to

http://www.domain/fr/contenu/titre-du-noeud-en-francais

So when I edit node I need to use url with language url setting

1. write the clean url alias: good readability & SEO but fail if the node title change.
http://www.domain/en/content/title-of-the-node-in-english
2. write the node url, without automatic title translation: bad readability, bad SEO, link ok if I change node title.
http://www.domain/en/node/23

With intlinks I wish I can write like case 2 with the advantages of the case 1.

The problem:

if I write "/node/23" the the url become :

http://www.domain/content/title-of-the-node-in-english

no langage path prefix, with pathauto setting and title url alias and with the good html title attribute.
so is a 404 error.

if I write "/en/node/23" the url become :

http://www.domain/en/node/23

no url alias, no html title attribute.
link is working.

Hope is it more clear now.
Please feel free to ask.

Thank you again to have taking my request in consideration.
best regards.

LoMo’s picture

Thanks for the follow up, mentat.fr. It provides sufficient detail that I should hopefully be able to replicate enough of your configuration to better understand your use case. I can't promise any timeline for making this more "i18n-friendly", but resolving this issue, or at least investigating what "best practices" resolution should be, is in my short-list of "to-dos".

BTW: I think the Internal Links module will probably work, as is, if you change from "language prefix" to "domain name" language switching, at least as long as each node has an assigned language . Using domain names has a number of benefits, not the least of which are SEO-related — and may also help with other such multilingual issues. I mention this only since if getting a suitable pair of domains for your languages is a feasible option, you may find the overall benefits are quite worthwhile. OTOH, I don't suggest this option in order for me to avoid the task of refactoring my code to handle your (not so uncommon) use case. ;-)

mentat.fr’s picture

Thanks to have put it in your top todo list !

Great solution ... you convinced me to test the subdomain url. My site is still in developpement, so I'll check it.

I understand well you don't want to avoid work ;)
but I'll give you some news about your solution.
Maybe it will working perfectly for me; if so the task will fall down in the bottom of your list !

Have a nice coding day.

mentat.fr’s picture

Hi LoMo,

I just test your solution and it's working very well, no problem ...
... apart I prefer the subfolders solution to preserve subdomain for applications like

http://www.forum.mydomain.tld/fr
http://www.forum.mydomain.tld/en

So if in futur intlinks will full compatible with Path prefix language code you'll make me a happy user ;)

Best regards.

LoMo’s picture

If you care about SEO and other issues that this might resolve, I'm guessing you'll end up preferring the benefits of language negotiation via domain. But I do want to fix this issue for people who are using language prefixes, too. I don't think it should be too difficult, really. I have an idea of what to do, but need to look at the database and experiment a bit to work out the details.

mentat.fr’s picture

I'll take reflexion before launch my site.
Indeed I know there is this short term solution.

Have nice day !

pozdneev’s picture

Issue tags: +multilanguage, +url alias, +path prefix

I have almost the same situation as mentat.fr.
The only difference is that "Path prefix language code" is empty for Russian and is "en" for English.

I believe, the algorithm should have the following steps.

1. Get the language of the page the link points to ({node} → language).
2. Get the path prefix for the language from the 1st step ({languages} → prefix). Note, that it could be empty.
3. Get the alias of the page the link points to ({url_alias} → alias).
4. Return [/{languages} → prefix]/{url_alias} → alias.

tempo22’s picture

subscribing

i really wish to see this feature because i'm working for some brand website who don't want to have differents domain for language but wish to keep brand.com/fr etc... :(

tempo22’s picture

I had to find a quick solution for this so i'm posting it in case someone have the problem
i had to modify the _intlinks_title_process_link function in intlinks_title_filter.inc file.

I added

 // Added to get the current node language
  $node_lang = '';
  $node_prefix = '';
  if ($node_id = intlinks_is_node_path($path_parts[0])) {
  	$node = node_load($node_id);
	if($node){
		$languages = language_list();
		$node_lang = $node->language;
		$node_prefix = $languages[$node->language]->prefix;
	}
  }
  
drupal_get_path_alias($node_path, $node_lang);
(empty($node_prefix)) ? '' : $node_prefix . '/') . $insert_path

to take care of path prefix and linking to other language pages ( by default drupal will link to /node/xx if the node is not from the language in use )
this modification is a quick fix so take care if you use it.


function _intlinks_title_process_link($matches) {
	
  if (($matches[2][0] != "/") && ($matches[2][0] != '.')) {
    // Do nothing if the link is not internal (root rel or doc rel).
    return $matches[0];
    // All links *should* be root relative.
  }
  

  $trimmed_path = ltrim($matches[2], "/");
  $path_parts = explode("#", $trimmed_path);
  
 
  // Added to get the current node language
  $node_lang = '';
  $node_prefix = '';
  if ($node_id = intlinks_is_node_path($path_parts[0])) {
  	$node = node_load($node_id);
	if($node){
		$languages = language_list();
		$node_lang = $node->language;
		$node_prefix = $languages[$node->language]->prefix;
	}
  }
  

  // If an HTML title attribute already exists...
  if (strpos($matches[1], 'title=') !== FALSE) {
     // and if the link is not to a "normal Drupal path" (path alias exists)...
    if (strpos($matches[2], '/node/') != 0) {
       // Do nothing if we already have a path alias and title.
      return $matches[0];
    }
    // Deal with links to normal Drupal paths which already have a title.
    // Change the Drupal path to a path alias, if one exists.
    elseif ($matches[2][0] == "/") {
       // The 2nd part of a normal Drupal path is the node id.
      if ($node_id = intlinks_is_node_path($path_parts[0])) {
        $node_path = 'node/' . $node_id;
        // Get alias, if one exists; otherwise, keep path as-is.		
		//edited to manage the lang prefix
        $path = drupal_get_path_alias($node_path, $node_lang);
        $insert_path = '/' . check_plain($path);
        // Exploding around '#' produces 2-element array for paths w/ anchors.
        if (count($path_parts) == 2) {
          $anchor = $path_parts[1];
          $insert_path .= '#' . $anchor;
        }
        // Insert the path alias into the link and return		
        return preg_replace('%href="([^"]+?)"%', 'href="' . ((empty($node_prefix)) ? '' : $node_prefix . '/') . $insert_path . '"', $matches[0]);
      }
      // Path starts with 'node/', but isn't a proper Drupal "normal path".
      // Return such links, unchanged.
      return $matches[0];
    }
    else {
       // No handling for document-relative links (for now).
       // @TODO: Placeholder to possibly add such handling.
      return $matches[0];
    }
  }
  else {
    // NO TITLE attribute exists.
    // Add title attribute to the link; then return the full link.
    // We need to get the node that the link refers to (if it's a node).
    if (strpos($matches[2], '/node/') === FALSE) {
      // Handle links which already have a path alias.
      // If the link is a normal, root-relative link, starting with "/":
      if ($matches[2][0] == "/") {
        $path_alias = $path_parts[0];
        // Calls to drupal_get_normal_path() return e.g. node/3 .
        $node_path = drupal_get_normal_path($path_alias);
        if (strpos($node_path, 'node/') === FALSE) {
          // The link must be an internal system path, views path, etc.
          // The link is not a node!! Keep the original link, unchanged.
          // @TODO: Get titles of any Views paths (e.g. page displays).
          return $matches[0];
        }
        // The 2nd part of a normal Drupal path is the node id.
        $node_id = ltrim($node_path, "node/");
        $node_title = intlinks_get_node_title_from_nid($node_id);
        if ($node_title === FALSE) {
          // Terminate -- something is wrong; all nodes should have titles.
          return $matches[0];
        }
        if (count($path_parts) == 1) {
          // There is no anchor in the link.
          // Insert the node title and return the new full link.
          // substr_replace('eggs','x',-1,-1); //eggxs  (Example from PHP API).
          return substr_replace($matches[0], ' title="' .
            check_plain($node_title) . '"', -1, -1);
        }
        else {
          // There is an anchor we want to add to the node title.
          // We will leave out adding the "anchor" text to the HTML title,
          // but leave the code here to modify as an "option".
          // @TODO: Optionally include any "anchor text" with HTML title value.
          // $anchor = ' (' . ucwords($path_parts[1]) . ')';
          $insert_link = ' title="' . check_plain($node_title) . '"';
          // And we need to insert the node title to return the new full link.
          // substr_replace('eggs','x',-1,-1); //eggxs  (Example from PHP API).
          return substr_replace($matches[0], $insert_link, -1, -1);
        }
      }
      else {
         // No handling for document-relative links. Return original link.
         // @TODO: Possibly add handling for document-relative internal links.
        return $matches[0];
      }
    }
    else {
     // The link is propably a 'node/2' -type path (normal Drupal path).
     // But, better safe than sorry: Give it a proper test.
      if ($node_id = intlinks_is_node_path($matches[2])) {
        $node_path = 'node/' . $node_id;
		//edited to manage the lang prefix
        $path = drupal_get_path_alias($node_path, $node_lang);
        $node_title = intlinks_get_node_title_from_nid($node_id);
        if ($node_title === FALSE) {
          // Terminate. Something is wrong; all nodes should have titles.
          return $matches[0];
        }
        $insert_title = ' title="' . check_plain($node_title) . '"';
		
		//edited to manage the lang prefix
		$insert_path = '/' . ((empty($node_prefix)) ? '' : $node_prefix . '/') . check_plain($path);
		
        // There is an anchor in the link if $path_parts array has 2 elements.
        if (count($path_parts) == 2) {
          $anchor = $path_parts[1];
          $insert_path .= '#' . $anchor;
        }
        $return_link = preg_replace('%href="([^"]+?)"%', 'href="' . $insert_path
          . '"', $matches[0]);
        return substr_replace($return_link, $insert_title, -1, -1);
      }
      else {
        // The link has 'node/' in its path, but it's not a node view.
        return $matches[0];
      }
    }
  }
}

mentat.fr’s picture

Thanks Tempo22
It would be nice if this feature become integrated.
Maybe you can propose a patch to LoMo ?

tempo22’s picture

Here is my try to make a patch.

It's my first patch in git for drupal so i hope it will work well

tempo22’s picture

Status: Active » Needs review

forgot to change the status

LoMo’s picture

Thanks for your work on that patch, Nicolas. It looks good... I'll need to test it, but I think it will break support for domain-based language negotiation. A lot of modules attempt to resolve multilingual issues by doing this (inserting a language prefix into the path), but since that's not the way all sites deal with multilingual content, it breaks the paths for sites using what I believe is the better method.

The primary company I do Drupal work for, Cocomore, has a Drupal-related site in English at http://drupal.cocomore.com . German content is on http://drupal.cocomore.de . By having separate domains, with links between what is seen as two different sites, we have good SEO benefits and it actually SOLVES many multilingual issues, e.g., the Internal Links filter works, as is, in this situation, as long as the language of both nodes (current content where the filter is running, and the linked node) are the same language (in the same domain. Inserting a language prefix, when that's not the way language negotiation is done on a particular site, can cause issues just as troublesome as complete lack of multilingual support, so the finished solution for this issue will need to involve checking the method of language negotiation and dealing with links appropriately. And since the same will apply to the "hide bad links" filter and any other filters added, I'll probably want to implement the logic as a "helper function" which deals with all the "multilingual logic".

So, for now, I'll move this back to "needs work"; but if your site uses prefix-based negotiation, this patch should work for you, for now. I need to take more time to figure out the best way to deal with this in a way that won't break links on any site...

tempo22’s picture

Status: Needs review » Needs work

mmm true, i made the fix for the site i was using on it. Sadly customer don't always listen to developer about the best pratice to use.
But in my exemple our website is in belgium. the domain will be .be but we will have /nl/ or /fr/ prefix based on the language choosen.

Anyway, when i get some more time i will give a look to make a more complete patch. I really want to get some training on git and drupal submit.

LoMo’s picture

I'd really like to implement this in a way which takes into account every method of translation. Drupal 7 is tricky since I guess you can also translate fields and paths with Internationalization, so there are many "ways to skin a cat" when it comes to content translation or languages associated with a path. So far I have not dealt with a really multilingual site in D7 and what I've experimented with has just been the "old D6 way" (where each node has a language). It's possible that whatever we do, we may need to compromise and say "nodes must have a language" for this module to be supported, but I'd rather try to find a way to deal with all multilingual methods and certainly want to support domain-based language negotiation.

Regarding Git: I started this module when we were using CVS here and that's still on my to-do list. I think I know my way around Git well enough now that I hope to be able to manage the patch and release process to provide some updates and the D6 version which I wrote ages ago, but didn't commit since I wasn't sure how to branch and tag in Git to create the D6 branch (it also needs multilingual support backported to it, now).

If anyone wants to help and is already reasonably good with Git, I'd be happy to have a co-maintainer. :-)

mentat.fr’s picture

tempo22, Lomo, thanks for the work.

During the waiting the perfect solution ;-),
I'll test tempo22's patch (I've an exclusive multilangual path solution) and make a report soon.

Robin Monks’s picture

If you're still looking for a co-maintainer to help update this module; let me know. We're using this with the patch in #13 on a client site with great results for the directory-based language selection method and I'd love to see the module handle either scenario natively.

/Robin

LoMo’s picture

Hi Robin,

Sorry for the delayed response... holidays and all, so I'm afraid it "slipped through the cracks". I'd love to have a co-maintainer for this since I'm not actually using it now. A second set of eyes and ideas would help provide the incentive to make this the "project of the day" one of these weekends. Ideally, I'd like to support every kind of language situation, including even functioning where translation is field-based and the title is provided by the Title module. It's a lot of changes and would likely have an impact on performance, so my thoughts are that we keep the 1.x branch for "lighter" mono-lingual installations (and also for perhaps simple multilingual sites with domain-based language negotiation and node-translation)... and create a 2.x branch with more complex negotiation of all the possible multilingual set-ups in D7. Ideally, it might also involve improved RegEx for identifying all elements of a path and an option (checkbox setting) for whether or not to consider absolute internal links internal or use them as an "escape" from the filter.

Other thoughts for improvements that I have considered are that for complex situations (e.g. links to non-node paths provided by various modules where it might be hard to get a title from the database without knowing where it's stored for every situation), we could use a header-request to get the page titles rather than a database query.

I actually made a number of improvements to this module, then didn't get around to committing them since I was not yet comfortable with Git. Now I just need to split up the changes into separate issue-related patches and determine what the next priorities are. Maybe I can get a start on that this week so this project doesn't look so abandoned. ;-)

BTW, I also created a 6.x version and emailed it to the guy who requested a back-port... long ago. But then also wasn't sure how to create a separate 6.x branch in Git/Drupal.org, so never got to that, either. I think I'm getting reasonably proficient enough with Git now to manage things like that.

Cheers,

Lowell

mkalkbrenner’s picture

patch #13 works here as well

LoMo’s picture

Status: Needs work » Needs review

I've made minor changes (formatting and getting rid of a notice that was displayed if a linked node did not have a language).

For now, I'll consider the multilingual issues "fixed", but we should create a separate issue for the field translation alternatives (i.e. with the Title module).

Crediting tempo22 for the commit. :-) (This will be in the "dev" branch shortly)

mkalkbrenner’s picture

Version: 7.x-1.1 » 7.x-2.x-dev
Component: Miscellaneous » Code
Assigned: Unassigned » mkalkbrenner
FileSize
9.58 KB

I refactored the code. I removed all the hand made stuff by using apis. That reduced the amount of code a lot and should add support for field translation.
The new implementation should support entity translation, the title module and all different language negotiations.
But the performance will be slower because we have to load every node targeted by a link. But on the other hand, if we assume that the filter results are cached in most installations, that's the better implementation.