4

When writing a custom module, what is the proper way to insert a localized menu via hook_menu or hook_menu_alter? By localized I mean not only the translated label - that'd be done using the t() function on title - but also having localized path and page callback.

I tried something like this:

function hook_menu() {
  global $language;
  $items = array();

  switch($language->language){
  case 'en':
    $items[] = array(
      'title' => 'Contact Us',
      'page callback' => 'drupal_goto',
      'page arguments' => array('node/285'),
      'access callback' => TRUE,
      'menu_name' => 'main-menu',
      'type' => MENU_NORMAL_ITEM,
    );
    break;
  case 'pl':
    $items[] = array(
      'title' => 'Kontakt',
      'page callback' => 'drupal_goto',
      'page arguments' => array('node/286'),
      'access callback' => TRUE,
      'menu_name' => 'main-menu',
      'type' => MENU_NORMAL_ITEM,
    );
    break;
  }
  return $items;
}

but this is not behaving properly - once created, the menu item stick at the original language regardless of subsequent language negotiations. So what is the way to go?


Edit after trying out the code provided in answers OK, so I approached that topic afresh and tried out all of the three methods suggested below. The two programmatic methods have the culprit that the array key used to install the menu item (as in: $items['corporate/contact-us']) will display in the status bar on hover, regardless of the language chosen. This is cosmetic but I consider it a bad UX. The third - UI-based - method has a side-effect (which is not the point of the original question) but was tried out just for curiosity. The point of my question was - whether it was possible to install a localized menu item totally programatically, and get exact result as when done through UI. Looks like this is not quite possible, unless there's a piece of know-how that I'm missing.

2

The array returned from hook_menu() is cached, and each implementation of hook_menu() is only invoked when the cache is cleared, which what happens, for example, when a module is disabled/enabled.

If the users are redirected to a different node depending on the currently set language, then the code should be similar to the following one.

function mymodule_menu() {
  $item['mymodule_path'] = array(
    'title' => 'Contact Us',
    'page callback' => 'mymodule_redirect',
    'access callback' => TRUE,
    'menu_name' => 'main-menu',
    'type' => MENU_NORMAL_ITEM,
  );

  return $items;
} 

function mymodule_redirect() {
  global $language;

  switch ($language->language) {
    case 'en':
      $goto = 'node/285';
      break;

    case 'pl':
      $goto = 'node/286';
      break;

    // ...
  }

  if (isset($goto)) {
    drupal_goto($goto);
  }
}

The alternative would be:

  • Create a link through the user interface (admin/structure/menu/manage/main-menu/add)

    screenshot

  • Use the following code to alter where the link takes

    function mymodule_menu_link_alter(&$item) {
      if ($item['module'] == 'menu' && $item['link_title'] == t('Contact us')) {
        $item['options']['alter'] = TRUE;
      }
    }
    
    function mymodule_translated_menu_link_alter(&$item, $map) {
      global $language;
    
      if ($item['module'] == 'menu' && $item['link_title'] == t('Contact us')) {
        switch ($language->language) {
          case 'pl':
            $item['link_path'] = 'node/286';
            break;
    
          // ...
        }
      }
    }
    

The first hook is necessary, or hook_translated_menu_link_alter() is not invoked.

The module could also programmatically add the link to the main menu. In this case, the code I would implement is similar to the following one.

function mymodule_enable() {
  $item = array(
    'link_path' => 'node/285',
    'link_title' => t('Contact us'),
    'menu_name' => 'main-menu',
    'mlid' => 0,
    'plid' => 0,
    'module' => 'mymodule',
  );

  menu_link_save($item);
}

function mymodule_menu_link_alter(&$item) {
  if ($item['module'] == 'mymodule' && $item['link_title'] == t('Contact us')) {
    $item['options']['alter'] = TRUE;
  }
}

function mymodule_translated_menu_link_alter(&$item, $map) {
  global $language;

  if ($item['module'] == 'mymodule' && $item['link_title'] == t('Contact us')) {
    switch ($language->language) {
      case 'pl':
        $item['link_path'] = 'node/286';
        break;

      // ...
    }
  }
}
  • Thanks kiamlaluno, this is a great answer. The only part I'm missing is whether I can programmatically deliver translations of the menu link title (mymodule_translated_menu_link_alter) or does it have to be done on the UI side? – Artur Dec 14 '12 at 14:12
  • You could, but it is rather better to translate "Contact us" from admin/config/regional/translate/translate. – kiamlaluno Dec 14 '12 at 20:50
  • thanks for your help. I added my findings in the question part to work around the comment limit. – Artur Dec 16 '12 at 23:35
0

The key to have a translated URL for a menu item is to create a language specific path alias for it. This can be done in PHP - with path_save() - when your module gets installed, or manually in the URL aliases configuration (/admin/config/search/path/add)

Then you can translate the menu item using common tactics of the i18n module bundle to have translated names of the menu item show up in your navigation. But this is independent of the translated URL path...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.