I'm really pleased with the way the new Drupal 4.7 menus and i18n (cvs) work together, as compared to the 4.6.x behaviour I experienced. Now when I create a menu item, point it at an existing URL alias without prefixing the locale (example: "mypage"), switch to my alternate language and then translate it with the locale module's string translator, it appears correctly on pages of both languages and, most importantly, the links are in the correct language ("en/mypage," "fr/mypage"). The only problem is that they don't expand, presumably because "en/mypage" is the URL alias, and "mypage" is the menu item's path, so technically they don't match. If only the link to "mypage" would expand if I clicked on "en/mypage" or "fr/mypage," this would be perfect. Otherwise, I will have to create two entirely separate menus, which is ridiculous and will add a pile of overhead to the menu admin functions.
Comments
Comment #1
klance commentedI did some digging, and this is what I found out. When I click on an "administer" menu item, the path returned looks like "admin" or "admin/menu" for example. But when I click on one of my custom menu items, the path returned looks like "node" or "node/14" for example. I became suspicious that this apparent inconsistency had something to do with the fact that the menu doesn't expand when custom menu items are selected.
Then there's the locale issue with i18n. A menu item's path is "mypath" when the URL alias is "en/mypath" so they don't match on a direct comparison. I theorized that if one or the other was changed so that they match, the menu would expand, and this was proven by changing the URL alias "en/mypath" to "mypath."
So here's what I did. In menu.inc there's a function on line 401 called menu_set_active_item that determines which menu items are expanded. By putting an "echo $path" at the beginning of this function I was able to watch the $path value passed to it as I clicked around. Noticing that different types of paths get passed to it by the admin menu than those passed by the custom menu, I added the following between lines 415 and 417:
global $locale;
// if the path starts with "node/"
if( substr( $path, 0, 5 ) == "node/" ) {
// get the url alias for the node path
$path = drupal_get_path_alias( $path );
// if the url alias is preceded by a locale
if( substr( $path, 0, 3 ) == "$locale/" ) {
// strip the locale so that the menu path will match
$path = substr_replace( $path, "", 0, 3 );
}
}
Essentially what this does is force the path to be an alias (i.e. admin/menu, en/mypage) instead of a system path (i.e. node, node/14) all the time. It also strips the locale off the $path if it exists, so that the menu item "mypage" matches "en/mypage" and "fr/mypage".
I'm sure there are more elegant ways of doing this, so don't hesitate to help me fix it up. Thanks!
Comment #2
klance commentedDarn... this seems to cause arbitrary "Access Denied" messages on pages.
Comment #3
klance commentedActually, this DOES work. I was getting "Access Denied" messages, but they were cause by the taxonomy_access module when View Uncategorized Nodes is disabled for unauthenticated users (an incompatibility with i18n, it looks like). If you allow uncategorized nodes to be displayed in taxonomy_access, or modify the path variable in a way similar to this menu.inc modification, the menus will expand.
Comment #4
klance commentedWith the code above in menu.inc, not only do the menus expand properly, but the breadcrumb trail works as well. The downfall so far is that for some reason, the Tabs do not appear which allow you to Edit, view Stats, etc.
Comment #5
klance commentedNice. It looks like the issue with Access Denied messages when viewing uncategorized nodes with View Uncategorized Nodes disabled, can be fixed by going into "category permissions" and re-saving the settings. I guess it goes through all the categories and resets the permissions when you do that.
Comment #6
klance commentedAllright. :-) Maybe I'm jumping the gun on this one. After reading lots, I've come to the conclusion that it isn't supposed to work like this, and I don't think I want to get into maintaining custom core code. I still, however, would like to be able to maintain a single menu for all languages, and translate only the text and have the links change and expand properly, and I think it's so doable that someone really familiar with the code could pull it off in a short time. So I'm moving this from a bug to a feature request.
Comment #7
mandclu commentedI tried to insert your hack, but things seem to have moved in the menu.inc included with 4.7.2. I did try a a number of places, and was able to create the unauthorized access problems, but never to get the menu to expand.
I did try checking the values that were being passed through the $path variable, and your code did seem to return the text of the url-alias, with the locale stripped out. It would unfailingly give me the Access Denied messages.
Hopefully someone can help me out and point out the painfully obvious, I can't for the life of me find where to allow viewing of uncategorized content.
I'd appreciate any help at all with this.
One other thing, is there any way to make the functions that compare the current item to the active trail to make them locale aware instead of altering what the system thinks the current location is? it just seems to me that we're altering a larger part of the system than we need to.
Comment #8
mandclu commentedJust clarification, I'm not using taxonomy_access or even taxonomy at all, so that's why I'm not sure where to resolve the access denied messages (it's not that I wasn't reading what was said eariler).
Comment #9
mandclu commentedOK, I did some more experimentation, and here's what seems to work for me in the menu_set_active_item function:
global $locale;
$orig_path = $_REQUEST['q'];
// if the url alias is preceded by a locale
$locale_length = strlen("$locale/");
if( substr( $orig_path, 0, $locale_length ) == "$locale/" ) {
// strip the locale so that the menu path will match
$orig_path = substr_replace( $orig_path, "", 0, $locale_length );
$ref_node = drupal_get_normal_path( $orig_path );
$path = $ref_node;
}
Note that this code also works for locale codes that are longer than two characters, such as those for Chinese.
I tried experimenting with something similar in i18nmenu.module in the i18nmenu_menu function, but it also cause the node content to revert to the original (untranslated) node. If someone knows of a way to do this that will only change the navigation, let me know.
I thought maybe something with menu_set_location(), but I'm not sure how to get the necessary array from the original node location. If someone had a thought on how to do this, it would allow for a fix that doesn't require changes to the core.
Comment #10
mandclu commentedOK, one more follow-up...
I managed to get it working by adding the following code to the end of the i18nmenu_menu function, in i18nmenu.module:
global $locale;
$orig_path = _i18n_get_original_path();
$locale_length = strlen("$locale/");
// if the url alias is preceded by a locale
if( substr( $orig_path, 0, $locale_length ) == "$locale/" ) {
// strip the locale so that the menu path will match
$orig_path = substr_replace( $orig_path, "", 0, $locale_length );
$ref_node['path'] = drupal_get_normal_path( $orig_path );
$ref_node['type'] = MENU_MODIFIABLE_BY_ADMIN;
menu_set_location($ref_node);
}
I must confess I wasn't 100% sure which type to specify, if anyone has any thoughts on this, please feel free to point out the error of my ways.
So this is a fix that doesn't require any changes to the core, and as a side benefit, using this approach the tabs for editing and translation appear.
Comment #11
klance commentedI've had a chance to look at this from a distance, and realized that I'm looking at it from the wrong point of view. There are no single, multilingual nodes, categories, or anything else in Drupal with i18n, so why should there be multilingual menus? I think the answer is not creating a single, multilingual menu, but rather smoothing out the process of translating menu items. But that's not i18n's problem. It's nice to create a page, then click Translate and create the translated version. I like that. I don't like going through the String translation process for menu items, though I do see the purpose of having the ability to translate strings. It would be cool, though, to be able to see menu item translations alongside their corresponding menu items instead of scrolling all over the place looking for menus that are in alphabetical order, and to be able to translate them more easily, even if the process is the same in the background. As for my menu hack, it sucks, and it opens up a can of worms I'd rather not bother with. I wouldn't waste any more time on it. Cheerz-K
Comment #12
klance commented