Here's a patch will try to translate menu paths using the translation_url function if the user's language is not the default. There are some other path translations that should be done (e.g. organic group submission pages), but that should be a change to the translation_url function.

CommentFileSizeAuthor
i18nmenu.patch.txt1.78 KBadixon

Comments

David Lesieur’s picture

Status: Reviewed & tested by the community » Needs work

This works well for me, with and without url aliases. Dare I say, i18nmenu is useless without this patch!

There seem to be one glitch though: when a menu item refers to a node in language A with no corresponding translation in language B, when switching to language B the menu item points to some irrelevant node in language B. I guess this should behave in accordance with the Content selection mode. For example, for the "Only current language and no language" mode, we should probably get a 404 page for the item when language B is the current language.

David Lesieur’s picture

This patch seems to also interfere somehow with module-generated paths. For example, all the menu items under 'administer' disappear when switching to a language other than English.

jax’s picture

This is exactly what I need as well. I've tested it with a page and your patch seems to alter the link to point to the page in the correct language. But, when I'm viewing a page in language X and I switch to language Y it will still show the page in the original language. Apparently the "language switcher" block doesn't obey your patch. I will see if I can provide a patch.

In stead of translating the URL when it is displayed I think another approach would be to translate it when that URL is actually displayed. For example:
node/1 => English page
node/2 => Dutch page
node/3 => French page

They are translations of each other.

When you go to the url "en/node/1" it shows you the english page. If you would go to nl/node/1 it would actually display node/2, and if you go to fr/node/1 it shows the french page. This works for all the combination, eg. en/node/3 would show you node/1.

The logic would be:
Is the node being requested in the current language?
yes > display
No > Is there a translation? Yes > show translation, No > show node.

Have you considered this approach? If so, why did you not opt for it? I'm in dire need of a solution for this, so I'll look into it as well.

adixon’s picture

Nice to see some feedback here.

I don't use the default i18n language switching block, I don't find it a good match for my client requirements, so I haven't tested how it interacts. But I think what you're looking at and talking about are problems with that switching, instead of the particular patch I'm providing.

I do agree that language switching is a bit of a problem - when there are possibilities of ending up on a page in one language with the interface in a different language, it really can get confusing, though I think there are valid use cases where you want this to happen, so I'm not sure that Jax's approach should be hard-coded, though perhaps it's a better default.

Conclusion: my patch uses the 'translation_url' function of the translation module, which is where all the problems identified here need to be addressed, I believe.

jax’s picture

First of all, your patch is adequate for what I need now, so that's great.

Your patch indeed calls the "translation_url" function on the already defined menu items. But what with menu items that are added after your hook is called? What with menu entries in blocks? Anyway, I would like to try to implement this since I think it almost comes down to calling the translate url function on the path before anything else happens. I'm just not having luck in finding the right hook to do this.

Is there a hook that allows you to alter the path (url)?

jax’s picture

Here is some code that does what I described above.

function i18nmenu_init() {
	global $i18n_langpath;
	$new = $i18n_langpath . "/" . translation_url(null, $i18n_langpath);
	$_REQUEST['q'] = $new;
}

This isn't the way to overwrite the request, but it works for me. If I put it in the translation.module (translation_init) it doesn't work anymore. So there should be some other way to implement this correctly.

Anyway, if you use this (in stead of the patch in this thread, not combined with) it will show the correct node. Following my example, if you go to nl/node/1 it will actually display node/2. This is another approach. I have no clue if this is a good approach....

jax’s picture

After some more thinking about this I can only conclude (with my limited knowledge of drupal) that this just cannot be solved perfectly with a module.

Your patch uses hook_menu to alter existing menu entries. But that is not the real purpose of hook_menu. It's "Define menu items and page callbacks". Also, menu entries that are added after your hook is called cannot be changed and links in blocks will be completely unaffected. The approach I suggested isn't really great either and also suffers from the fact that it isn't executed before everything else.

You need to have i18n support in the core to make this work every time. I think that using the translation_url function in the l() function might be a solution. Then every link placed on the page will be translated. But then again, what do I know ;)

adixon’s picture

Your patch uses hook_menu to alter existing menu entries. But that is not the real purpose of hook_menu. It's "Define menu items and page callbacks". Also, menu entries that are added after your hook is called cannot be changed and links in blocks will be completely unaffected. The approach I suggested isn't really great either and also suffers from the fact that it isn't executed before everything else.

What you say about hook_menu is true, sort of. My patch just modifies the existing i18n menu, so issues about the mechanism should really be taken up with jose (i18nmenu author). In any case, this mechanism will work on all cacheable items, it just won't affect non-cacheable ones that are inserted after i18nmenu has been called.

Another way to do this, which I implemented originally but isn't so useful for others, is to do it in your theme - that way you can guarantee that it'll try and translate all items. Aside from not being as portable or easily implemented, that approach also suffers from the lack of caching ...

jax’s picture

Jose (or anyone else in the know), is there any reason why this isn't being included? I'm actually using this patch often!

saml’s picture

This seems to still not have been included in the Drupal 5.0 version?
I miss it!

Thanks for the good work
// Samuel Lampa

jose reyero’s picture

I don't really see this as a clean implementation for menu translation. But anyway, that i18nmenu.module is kind of a dirty hack to be dropped sometime soon when we find some better option.

So, ok, if you all agree this is some improvement, I'll get this into the module, just needs some clean up. What's that hardcoded 'en' language code? This should be only dependent on 'default language' which is not always English.

But about menu translation in general, I'd better use one of these two options:
- Creating whole menus for each language then displaying them in different blocks, visible for en/*, es/*, etc..
- Using the new 'per language menu items' which is already into the module. (Have you tried it?)

jax’s picture

On one site I have used the i18nmenu module with this patch. The advantage is that you only have to define your menu once, the rest is done through localization and by the module. This is clean. There is a usability issue because the menu fieldset appears on all languages. But else this is a nice solution.

When you use menu_primary_menu as language dependent you need to define your menu 3 times and in de admin overview menu all the menu entries appear (drupal 5) without any indication of language. So you have to click it to see which language it is. This solution is only interesting if your menu's are different per language.

So currently my preference is the option where the module takes care of everything.

One other thing is that the language switch block should do this as well. Now when you switch language it doesn't go to the translation of the node you are currently viewing if it is available. I actually need this functionality for a site I'm working on so if this doesn't exist yet I'll create a patch.

davemybes’s picture

Jax, try using the Translation block instead of the Language switcher block.

jax’s picture

Man, has that block always been there?! Can't believe that I missed that. Thanks incrn8!

adixon’s picture

re: the 'en' hardcoding in this patch. It's actually just a placeholder (as noted in the code), and isn't dependent on the default language. It could be changed to 'none' to be clearer, though 'en' really is the default or non-specific language of drupal, so it's appropriate in that sense - i.e. it's what you get if you don't run t() on any of the strings, which is analogous to what this placeholder achieves.

jose reyero’s picture

Version: master » 5.x-2.x-dev

Just tagging for 5.x, not needed anymore for 6.

mr.j’s picture

Component: Module i18nmenu » Code

Hi there, this is still a problem in 5.x.

Just thought I would list a succinct description of all the problems I have run into with this. The scenario:

We have one node about apples in English
path: /en/apples
title: "Apples"

It is translated to German (de)
path: /de/apfel
title: "Äpfel"

So in our menu, we must currently have 2 separate menu items with a defined language set, linking to each of the respective node translations. Imagine we have 10 items in our menu, each translated into 5 languages. We now have 50 menu items to maintain (and when in menus admin, you see a long menu with every item from every language regardless of your language setting).

We could create 5 separate menus (one for each language) - but this is a lot of extra work, and is not ideal if you add an untranslated node that you want to appear in every language's menu because obviously you have to add a menu item for it 5 times.

If we use i18nmenu and just use one menu item per node without a defined language set, then i18nmenu happily translates the title of the menu item when we switch languages using the localisation strings settings. It does not translate the path. So in English it points to en/Apples and in German it points to de/Apples (the wrong node).

Now as for the old patch on this issue, the addition of menu items after this patch's hook has run should have been easy to solve - set the module's weight to 10. However it looks like it introduces a whole heap of other issues, as well as missing menus which are not run through hook_menu, which means patching core looks unavoidable to fix this properly in 5.x.

mr.j’s picture

This patch to menu.inc works for me on drupal 5.x:

Index: menu.inc
===================================================================
--- menu.inc	(revision 37)
+++ menu.inc	(working copy)
@@ -1090,6 +1090,10 @@
       // Handle URL aliases if entered in menu administration.
       if (!isset($_menu['path index'][$item->path])) {
         $item->path = drupal_get_normal_path($item->path);
+        /* Patch: enables single menu item to point to translations */
+        if (module_exists('i18n') && module_exists('translation')) {
+          $item->path = translation_url($item->path, i18n_get_lang());
+        }
       }
       if (isset($_menu['path index'][$item->path])) {
         // The path is already declared.
mr.j’s picture

I also had to patch i18n.module to prevent it from giving a node's menu item the node's language each time you save the node. Otherwise everything with the above patch is great until you save a node with the menu item on it and then i18n assigns the menu item a language and you never see that menu item again in your other language menus (because it no longer has "no language").

You can still give a menu item a language - you just have to do it using the menu item edit page rather than via the node edit page.

Index: i18n.module
===================================================================
--- i18n.module	(revision 37)
+++ i18n.module	(working copy)
@@ -191,12 +191,12 @@
           $item['type'] = $item['type'] | MENU_MODIFIED_BY_ADMIN;
           if ($item['mid']) {
             // Update menu item 
-            db_query("UPDATE {menu} SET pid = %d, path = '%s', title = '%s', description = '%s', weight = %d, type = %d, language = '%s' WHERE mid = %d", $item['pid'], $item['path'], $item['title'], $item['description'], $item['weight'], $item['type'], $node->language, $item['mid']);
-            drupal_set_message(t('The menu item %title has been updated with node language.', array('%title' => $item['title'])));
+            db_query("UPDATE {menu} SET pid = %d, path = '%s', title = '%s', description = '%s', weight = %d, type = %d WHERE mid = %d", $item['pid'], $item['path'], $item['title'], $item['description'], $item['weight'], $item['type'], $item['mid']);
+//            drupal_set_message(t('The menu item %title has been updated with node language.', array('%title' => $item['title'])));
           } elseif(SAVED_NEW == menu_save_item($item)) {
             // Creating new menu item with node language
-            db_query("UPDATE {menu} SET language = '%s' WHERE mid = %d", $node->language, $item['mid']);
-            drupal_set_message(t('The menu item %title has been added with node language.',  array('%title' => $item['title'])));         
+//            db_query("UPDATE {menu} SET language = '%s' WHERE mid = %d", $node->language, $item['mid']);
+//            drupal_set_message(t('The menu item %title has been added with node language.',  array('%title' => $item['title'])));         
           }
           menu_rebuild();
           unset($node->menu); // Avoid further processing by menu module
jose reyero’s picture

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

Just critical bug fixes for 5.x.

ingomueller.net’s picture

Hi everyone!

I just spent a week figuring out how to do translated menus properly. I want to share this solution on a few pages I did NOT find the solution on.

There where always problems with either menu items with the same path for both languages (like /contact), which required a single, translated menu item, and items with different paths for the two languages (like node/10 and node/11 for the translation), which required two seperated menu items, one per language. There are situations where you can't combine the two approaches.

Here's a third one. Instead of having two different menu ITEMs, have two different MENUs. Select which one to use for each language by specifying that "menu_primary_menu" system variable is multilingual. Problem solved. Everything works fine for me.

See:
http://drupal.org/node/134002
http://becircle.com/translating_menus_drupal_6

Hope this helps...
Ingo