You are here

i18n_menu_node.module in Menu translation - Node 7

Menu translation (Node).

Allows a single node menu item to point to different node translations depending on the current language.

@author Francesco Placella, http://www.psegno.it, 2010

File

i18n_menu_node.module
View source
<?php

/**
 * @file
 * Menu translation (Node).
 *
 * Allows a single node menu item to point to different node translations
 * depending on the current language.
 *
 * @author Francesco Placella, http://www.psegno.it, 2010
 */

/**
 * Every [i18n] block delta has the form <module><separator><delta>.
 */
define('I18N_MENU_NODE_BLOCK_SEPARATOR', '--');

/**
 * Implementation of hook_init().
 */
function i18n_menu_node_init() {
  if ($node = menu_get_object()) {
    i18n_menu_node_set_item($node);
  }
}

/**
 * Return the information used to provide the [i18n] blocks.
 */
function i18n_menu_node_block_module_info($module = NULL) {
  static $info;
  if (!isset($info)) {
    $info = array(
      'menu' => array(
        'view' => 'i18n_menu_node_menu_block_view',
      ),
      'user' => array(
        'delta' => array(
          1,
        ),
        'view' => 'i18n_menu_node_navigation_menu_block_view',
      ),
      'menu_block' => array(),
    );
    drupal_alter('i18n_menu_node_block_info', $info);
  }
  return empty($module) ? $info : $info[$module];
}

/**
 * Implementation of hook_block_info().
 *
 * Provide an i18n version of each menu block.
 */
function i18n_menu_node_block_info() {
  $blocks = array();
  $schema = drupal_get_schema('block');
  $max_length = $schema['fields']['delta']['length'];
  $separator = I18N_MENU_NODE_BLOCK_SEPARATOR;

  // Retrieve blocks from all the modules defined in the block information.
  foreach (i18n_menu_node_block_module_info() as $module => $data) {
    $module_blocks = module_invoke($module, 'block_info');
    if (!empty($module_blocks)) {

      // If a delta set is specified filter out the undesired blocks.
      if (!empty($data['delta'])) {
        $module_blocks = array_intersect_key($module_blocks, array_flip($data['delta']));
      }

      // Create an [i18n] version for each block and store the parent module
      // as part of the delta.
      foreach ($module_blocks as $delta => $block) {

        // Truncate the [i18n] block delta to the maximum allowed length and
        // check that there is no block with such a delta already defined.
        $mtn_delta = substr($module . $separator . $delta, 0, $max_length);
        if (!isset($blocks[$mtn_delta])) {
          $blocks[$mtn_delta]['info'] = t('[i18n] !block', array(
            '!block' => $block['info'],
          ));

          // Inherit block settings except for the 'info' key.
          $blocks[$mtn_delta] += $block;
        }
        else {
          $limit = $max_length - strlen($module . $separator);
          $info = str_replace('[i18n] ', '', $blocks[$mtn_delta]['info']);
          drupal_set_message(t('The [i18n] version of <em>@current_block</em> cannot be provided: the block identifier is already assigned to <em>@block</em>. Try to change the original block identifier before character !limit.', array(
            '@current_block' => $block['info'],
            '@block' => $info,
            '!limit' => $limit,
          )), 'warning', FALSE);
        }
      }
    }
  }
  return $blocks;
}

/**
 * Implementation of hook_block_view().
 *
 * Provide an i18n version of each menu block.
 *
 * Since menu tree rendering is influenced by language selection modes we need
 * to temporarily disable language selection.
 *
 */
function i18n_menu_node_block_view($delta = '') {

  // A longer separator would risk exceed the delta column max length.
  $separator = I18N_MENU_NODE_BLOCK_SEPARATOR;
  list($module, $delta) = explode($separator, $delta, 2);
  $info = i18n_menu_node_block_info($module);
  if (!empty($info['file'])) {
    require_once $info['file'];
  }
  $block = NULL;

  // If a view callback is defined we use it, otherwise we fall back to the
  // usual hook_block_view() invocation.
  i18n_select(FALSE);
  if (!empty($info['view']) && function_exists($info['view'])) {
    $block = $info['view']($delta);
  }
  else {
    $block = module_invoke($module, 'block_view', $delta);
  }
  i18n_select(TRUE);
  return $block;
}

/**
 * Render the navigation menu block.
 */
function i18n_menu_node_navigation_menu_block_view() {
  global $user;
  $block = i18n_menu_node_menu_block_view('navigation');

  // Authenticated users have their name as title of the navigation menu.
  if ($user->uid) {
    $block['subject'] = check_plain($user->name);
  }
  return $block;
}

/**
 * Render the i18n version of a menu block.
 *
 * To render the menu tree we use i18nmenu_translated_tree(). With this approach
 * we avoid rendering an unlocalized version and then replacing it during block
 * preprocessing, hence we should have a performance improvement here over the
 * standard (translated) menu blocks.
 *
 * @see menu_tree_check_access().
 */
function i18n_menu_node_menu_block_view($menu_name) {
  static $menus;
  if (!isset($menus)) {
    $menus = menu_get_menus();
  }
  $block = array();
  $block['subject'] = check_plain($menus[$menu_name]);
  $block['content'] = i18n_menu_translated_tree($menu_name);
  return $block;
}

/**
 * Implementation of hook_theme_registry_alter().
 *
 * Rebuild the menu system static cache before the primary and secondary links
 * variables are populated.
 *
 * @see i18n_menu_node_navigation_links_prepare().
 */
function i18n_menu_node_theme_registry_alter(&$theme_registry) {
  array_unshift($theme_registry['page']['preprocess functions'], 'i18n_menu_node_navigation_links_prepare');
}

/**
 * Force the menu system to build its static cache without language selection.
 *
 * By switching temporarily off language selection we avoid hiding menu node
 * items with a language not matching the current language selection mode. This
 * must be called before the static cache in menu_tree_page_data() is populated
 * to be effective.
 */
function i18n_menu_node_navigation_links_prepare() {
  i18n_select(FALSE);
  if (theme_get_setting('toggle_primary_links')) {
    menu_main_menu();
  }
  if (theme_get_setting('toggle_secondary_links')) {
    menu_secondary_menu();
  }
  i18n_select(TRUE);
}

/**
 * Implementation of hook_menu_link_alter().
 *
 * Prepare node translation.
 */
function i18n_menu_node_menu_link_alter(&$item) {

  // Check if node translation is enabled.
  $node_translation = !empty($item['i18n_menu_node_translation']) || !isset($item['i18n_menu_node_translation']) && isset($item['options']['translations']);

  // Remove translations: they will be rebuilt below, if necessary.
  unset($item['options']['translations']);

  // If we are handling custom menu items of menu module and no language is set,
  // prepare node translation data.
  if ($node_translation && (empty($item['language']) || $item['language'] == 'und') && $item['module'] == 'menu' && ($nid = _i18n_menu_node_check_path($item))) {

    // Get the translation set id from the menu item path.
    if ($tnid = _i18n_menu_node_get_tnid($nid)) {
      $segments = explode('/', $item['link_path']);

      // We always store the source node nid to allow a correct handling of
      // the active trail in i18n_menu_node_nodeapi() and to improve
      // performance in i18n_menu_node_item_translations_refresh_set().
      $segments[1] = $tnid;

      // Use the source node nid in the link path.
      $item['link_path'] = implode('/', $segments);

      // We store here the current translation set to allow later translation.
      $item['options']['translations'] = i18n_menu_node_get_translations($tnid, NULL);

      // Set alter variables so hook_translated_menu_link_alter() is called
      $item['options']['alter'] = 1;
      $item['localized_options']['alter'] = 1;
    }
    else {

      // No translations available yet but node translation for this menu item
      // is enabled.
      $item['options']['translations'] = array();
    }
  }
}

/**
 * Check that the given item has a valid node path.
 */
function _i18n_menu_node_check_path($item) {
  $segments = explode('/', $item['link_path']);
  return $segments[0] == 'node' && isset($segments[1]) && intval($segments[1]) ? $segments[1] : FALSE;
}

/**
 * Return the translation set id for the give nid.
 */
function _i18n_menu_node_get_tnid($nid) {
  static $tnids = array();
  if (!isset($tnids[$nid])) {
    $result = db_query("SELECT n.tnid FROM {node} n WHERE n.nid = :nid", array(
      ':nid' => $nid,
    ))
      ->fetchAssoc();
    $tnids[$nid] = $result['tnid'];
  }
  return $tnids[$nid];
}

/**
 * Implementation of hook_translated_menu_link_alter().
 *
 * Perform node translation.
 *
 * @see i18n_menu_node_menu_link_alter()
 */
function i18n_menu_node_translated_menu_link_alter(&$item, $map) {
  if ($item['module'] == 'menu' && ($map[1] = i18n_menu_node_translation($item, $map))) {
    $item['href'] = implode('/', $map);
  }
}

/**
 * Return the nid of the node translation for the given language if available.
 */
function i18n_menu_node_translation($item, $map, $langcode = NULL) {
  if (empty($item['options']['translations'])) {
    return FALSE;
  }

  // If the current item is also the active item, in $map[1] we have a node
  // object instead of a simple nid.
  $nid = is_object($map[1]) ? $map[1]->nid : $map[1];
  if (empty($langcode)) {

    // Retrieve the current language.
    $langcode = i18n_language()->language;
  }

  // If the translation is available, is not the current element and translation
  // support for its node type is enabled, we can use it.
  // For performance reasons we check only if the node status is published or
  // if the user can see unpublished nodes. Uncommon cases of fine-grained
  // access-controlled translated node menu items might lead to "access denied"
  // pages.
  $translations = $item['options']['translations'];
  if (is_array($translations) && isset($translations[$langcode])) {
    $translation = $translations[$langcode];
    if ($translation->nid != $nid && translation_supported_type($translation->type) && ($translation->status || user_access('administer nodes'))) {
      return $translations[$langcode]->nid;
    }
  }
  return FALSE;
}

/**
 * Implementation of hook_form_FORM_ID_alter().
 *
 * Add a checkbox to the menu_edit_menu form to enable path translation for node
 * links and register a submit callback to process menu title.
 */
function i18n_menu_node_form_menu_edit_menu_alter(&$form, $form_state) {
  $form['submit']['#weight'] = 9;
  $form['i18n_menu_node_translation'] = array(
    '#type' => 'checkbox',
    '#title' => t('Enable node translation by default'),
    '#description' => t('If this is checked the menu item forms will have the <em>Enable node translation</em> option checked by default.'),
    '#default_value' => i18n_menu_node_translation_enabled(isset($form['menu_name']['#value']) ? $form['menu_name']['#value'] : $form['menu_name']['#default_value']),
  );
  $form['#submit'][] = 'i18n_menu_node_menu_edit_menu_submit';
}

/**
 * Submit handler for the menu_edit_menu form.
 */
function i18n_menu_node_menu_edit_menu_submit($form, &$form_state) {

  // Save the node translation setting for the current menu.
  $menu = ($form['#insert'] ? 'menu-' : '') . $form['menu_name']['#value'];
  $value = $form_state['values']['i18n_menu_node_translation'];
  variable_set("i18n_menu_node_translation_{$menu}", $value);
}

/**
 * Implementation of hook_form_FORM_ID_alter().
 *
 * Add a checkbox to enable node translation.
 */
function i18n_menu_node_form_menu_edit_item_alter(&$form, $form_state) {

  // Node translation has no meaning when one explicitly assigns a language to a
  // menu item.
  // NEED TO PORT DEFAULT BEHAVIOR
  if (!isset($form['menu']['#item']) || empty($form['menu']['#item']['options']['langcode'])) {
    $router_item = menu_get_item();
    $default = !empty($form['menu']['#item']) ? i18n_menu_node_enabled($form['menu']['#item']) : i18n_menu_node_translation_enabled($router_item['page_arguments'][3]['menu_name']);
    $form['i18n_menu_node_translation'] = array(
      '#type' => 'checkbox',
      '#title' => t('Enable node translation'),
      '#description' => t('If this is checked node link paths will be replaced with the node translation paths.'),
      '#default_value' => $default,
    );
    $form['#validate'][] = 'i18n_menu_node_form_menu_edit_item_validate';
  }
}

/**
 * Validation handler for the menu item edit form.
 */
function i18n_menu_node_form_menu_edit_item_validate($form, &$form_state) {
  $item = $form_state['values'];
  if ($item['i18n_menu_node_translation']) {
    if (!_i18n_menu_node_check_path($item)) {
      form_set_error('menu][link_path', t('Node translation can be enabled only for node paths.'));
    }
    if (!empty($item['language']) && $item['language'] != 'und') {
      form_set_error('menu][language', t('Node translation cannot be enabled on items having a language associated.'));
    }
  }
}

/**
 * Implementation of hook_form_alter().
 */
function i18n_menu_node_form_alter(&$form, &$form_state, $form_id) {
  if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] . '_node_form' == $form_id && isset($form['menu'])) {
    $node = $form['#node'];
    $translation_support = translation_supported_type($node->type);
    $node_translation_form = array(
      '#type' => 'checkbox',
      '#title' => t('Enable translations'),
      '#description' => t('If this is checked the menu item will be shared among all the node translations, however you will still be able to translate the menu link title.'),
      '#weight' => 0,
    );

    // If the node already has a menu just add the node translation option, but
    // only for source nodes.
    if (!empty($node->menu['mlid']) && $translation_support && (empty($node->tnid) || $node->nid == $node->tnid)) {
      $node_translation_form['#default_value'] = i18n_menu_node_enabled($node->menu);
      $form['menu']['link']['i18n_menu_node_translation'] = $node_translation_form;
      $form['menu']['link']['link_title']['#weight'] = 0;
      return;
    }
    else {
      $menu_name = variable_get('menu_default_node_menu', 'primary-links');
      $node_translation_form['#default_value'] = i18n_menu_node_translation_enabled($menu_name);
    }

    // Select the current translation set id.
    $tnid = _i18n_menu_node_get_node_tnid($node);

    // Get the original node translation.
    if (!empty($tnid) && (!isset($node->nid) || $node->nid != $tnid)) {
      $tnode = node_load($tnid);
    }
    else {
      if ($translation_support) {
        $form['menu']['link']['i18n_menu_node_translation'] = $node_translation_form;
      }
      return;
    }

    // Prepare the tnode so the menu item will be available.

    //_i18n_menu_node_prepare($tnode);

    // CHECK PORTED CODE
    menu_node_prepare($tnode);
    if ($translation_support && $tnode->menu && i18n_menu_node_enabled($tnode->menu)) {

      // If node translation is enabled we share one single menu item among
      // all the nodes belonging to the translation set.
      $form['menu']['link']['link_title_translation'] = $form['menu']['link']['link_title'];
      $form['menu']['link']['link_title_translation']['#title'] = t('Menu item translation');
      $form['menu']['link']['link_title_translation']['#description'] = t('The translation of the menu item title.');
      $langcode = empty($node->translation_source) ? $form['language']['#default_value'] : $node->translation_source->language;
      $default = _i18n_menu_node_get_item_title($tnode->menu, TRUE, $langcode);
      if (!empty($default)) {
        $form['menu']['link']['link_title_translation']['#default_value'] = $default;
        $form['menu']['#collapsed'] = 0;
      }

      // Translating the menu item title requires only the 'translate interface'
      // permission and not the 'administer menu' one.
      $form['menu']['#access'] = user_access('translate interface');
      unset($form['menu']['link']['link_title']);
      unset($form['menu']['link']['parent']);
      unset($form['menu']['link']['weight']);
      unset($form['menu']['link']['options']);
      unset($form['menu']['enabled']);
      unset($form['menu']['link']['#states']['invisible']);
      $form['#tnode'] = $tnode;
      $form['#submit'][] = 'i18n_menu_node_form_node_submit';
    }
  }
}

/**
 * Optionally insert/update and return a localized menu item title.
 */
function _i18n_menu_node_get_item_title($link, $update = FALSE, $langcode = NULL) {
  $key = 'menu:item:' . $link['mlid'] . ':title';
  if ($update) {
    i18n_string_update($key, $link['link_title']);
  }
  return i18n_string($key, $link['link_title'], array(
    'langcode' => $langcode,
  ));
}

/**
 * Return the translation set id from the given node.
 */
function _i18n_menu_node_get_node_tnid($node) {

  // CHECK PORTED CODE
  if (empty($node->translation_source)) {
    return empty($node->tnid) ? '' : $node->tnid;
  }
  else {

    // If the tnid is empty we are creating the first translation.
    return empty($node->translation_source->tnid) ? $node->translation_source->nid : $node->translation_source->tnid;
  }
}

/**
 * Submit handler for the node form.
 */
function i18n_menu_node_form_node_submit($form, &$form_state) {
  $values =& $form_state['values'];
  $values['status_changed'] = $form['#node']->status != $values['status'];
  if (!empty($form['#tnode']->menu['link_title']) && !empty($values['menu']['link_title_translation'])) {

    //i18nstrings_save_translation($values['language'], $form['#tnode']->menu['link_title'], $values['menu']['link_title_translation'], 'menu');
    $key = 'menu:item:' . $form['#tnode']->menu['mlid'] . ':title';
    i18n_string_translation_update($key, $values['menu']['link_title_translation'], $values['language']);
  }
}

/**
 * Implementation of hook_node_presave().
 */
function i18n_menu_node_node_presave($node) {

  // Ensure that the menu item language is always set to 'All languages' if
  // node translation is enabled.
  if (isset($node->menu) && !empty($node->menu['i18n_menu_node_translation'])) {
    unset($node->menu['language']);
    unset($node->menu['options']['langcode']);
  }
}

/**
 * Implementation of hook_node_insert().
 */
function i18n_menu_node_node_insert($node) {
  if (isset($node->translation_source)) {
    i18n_menu_node_item_translations_refresh_set(_i18n_menu_node_get_node_tnid($node));
  }
}

/**
 * Implementation of hook_node_update().
 */
function i18n_menu_node_node_update($node) {
  if ($node->tnid && !empty($node->status_changed)) {
    i18n_menu_node_item_translations_refresh_set($node->tnid);
  }
}

/**
 * Implementation of hook_node_delete().
 */
function i18n_menu_node_node_delete($node) {
  if ($node->tnid) {

    // @todo: update new source translation node if the translation set is
    // empty.
    i18n_menu_node_item_translations_refresh_set($node->tnid);

    // Remove the menu translation for the corresponding node translation.
    if ($node->nid != $node->tnid) {
      $tnode = (object) array(
        'nid' => $node->tnid,
      );
      $tnode = node_load($tnode->nid);
      menu_node_prepare($tnode);
      _i18n_menu_node_delete_item_translation($tnode->menu['mlid'], $node->language);
    }
  }
}

/**
 * Implementation of hook_context_plugins().
 */
function i18n_menu_node_context_plugins() {
  $plugins = array();
  $plugins['i18n_menu_node_context_condition'] = array(
    'handler' => array(
      'path' => drupal_get_path('module', 'i18n_menu_node') . '/plugins',
      'file' => 'i18n_menu_node_context_condition.inc',
      'class' => 'i18n_menu_node_context_condition',
      'parent' => 'context_condition_menu',
    ),
  );
  return $plugins;
}

/**
 * Implementation of hook_context_registry().
 */
function i18n_menu_node_context_registry_alter(&$registry) {
  if (isset($registry['conditions']['menu'])) {
    $registry['conditions']['menu']['plugin'] = 'i18n_menu_node_context_condition';
  }
}

/**
 * Implementation of hook_token_info().
 */
function i18n_menu_node_token_info() {
  $info['tokens']['node']['menu-link-i18n'] = array(
    'name' => t('Menu link translated'),
    'description' => t("The unfiltered menu path (as reflected in the breadcrumb), not including Home or [menu]."),
    'type' => 'array',
  );
  return $info;
}

/**
 * Implementation of hook_tokens().
 */
function i18n_menu_node_tokens($type, $tokens, $data = array(), $options = array()) {
  $replacements = array();
  if ($type == 'node' && !empty($data['node'])) {
    $node = $data['node'];
    $trail_raw = array();
    $item = FALSE;

    // If there are menu-link-i18n tokens
    if ($parents_tokens = token_find_with_prefix($tokens, 'menu-link-i18n')) {

      // If the current node has no menu item we use the source node one.
      if (empty($node->menu['mlid'])) {
        $tnid = _i18n_menu_node_get_node_tnid($node);
        if (!empty($tnid) && $tnid != $node->nid) {
          $tnode = (object) array(
            'nid' => $tnid,
          );
          menu_node_prepare($tnode);

          // We use the source node menu item only if node translation is enabled.
          $item = !empty($tnode->menu['mlid']) && i18n_menu_node_enabled($tnode->menu) ? $tnode->menu : FALSE;
        }
      }
      else {
        $item = $node->menu;
      }
      while ($item) {
        array_unshift($trail_raw, _i18n_menu_node_get_item_title($item, FALSE, $node->language));
        $item = empty($item['plid']) ? FALSE : menu_link_load($item['plid']);
      }
      $replacements += token_generate('array', $parents_tokens, array(
        'array' => $trail_raw,
      ), $options);
    }
  }
  return $replacements;
}

/**
 * Implementation of hook_form_FORM_ID_alter().
 *
 * Handle addition/deletions to the translation set performed from the "Select
 * translation" i18n form.
 */
function i18n_menu_node_form_i18n_node_select_translation_alter(&$form, $form_state) {
  $form['#submit'][] = 'i18n_menu_node_translation_set_update';
}

/**
 * Submit handler for i18n_node_select_translation form.
 *
 * Refresh menu items' translation cache if the translation set has changed.
 */
function i18n_menu_node_translation_set_update($form, &$form_state) {
  $changed = FALSE;
  $nids = array(
    $form['node']['#value']->nid,
  );
  foreach ($form_state['values']['translations']['nid'] as $langcode => $nid) {
    $previous = $form['translations']['nid'][$langcode]['#value'];
    if ($previous != $nid) {
      $changed = TRUE;
    }
    if ($previous) {
      $nids[] = $previous;
    }
    if ($nid) {
      $nids[] = $nid;
    }
  }
  if ($changed) {
    $nids = array_unique($nids);
    i18n_menu_node_item_translations_refresh($nids);
  }
}

/**
 * Refresh the translation cache for any menu item belonging to the translation
 * sets identified by the given tnid.
 */
function i18n_menu_node_item_translations_refresh_set($tnid) {
  $nids = i18n_menu_node_get_translations($tnid);
  if (empty($nids)) {
    $nids = array(
      $tnid,
    );
  }
  i18n_menu_node_item_translations_refresh($nids);
}

/**
 * Refresh the translation cache for any menu item belonging to the translation
 * sets identified by the given nids.
 */
function i18n_menu_node_item_translations_refresh($nids) {
  $query = db_select('menu_links', 'm');
  $query
    ->fields('m', array(
    'mlid',
  ));
  $or = db_or();
  foreach ($nids as $nid) {
    $or
      ->condition('link_path', 'node/' . $nid, '=');
  }
  $query
    ->condition($or);
  $query
    ->orderBy('mlid');
  $result = $query
    ->execute()
    ->fetchAssoc();
  _i18n_menu_node_item_translations_refresh($result);
}

/**
 * Refresh the translation cache for any menu item pointing to a node page.
 */
function i18n_menu_node_item_translations_refresh_all($force_status = NULL) {

  // @todo: Consider using a batch process here.
  $result = db_select('menu_links', 'm')
    ->fields('m', array(
    'mlid',
  ))
    ->condition('link_path', 'node/%', 'LIKE')
    ->orderBy('mlid')
    ->execute()
    ->fetchAssoc();
  _i18n_menu_node_item_translations_refresh($result, $force_status);
  watchdog('i18n_menu_node', 'Translation cache has been refreshed for all node menu items.', array(), WATCHDOG_INFO);
}

/**
 * Helper function: actually refresh the menu items returned by the given query.
 */
function _i18n_menu_node_item_translations_refresh($result, $force_status = NULL) {
  $list = module_list(FALSE, FALSE);
  if (isset($result) && is_array($result)) {
    foreach ($result as $mlid) {

      // Refresh the item translation cache.
      $item = menu_link_load($mlid);
      if (isset($force_status)) {
        $item['i18n_menu_node_translation'] = $force_status;
      }

      // If node translation is disabled we manually call hook_menu_link_alter()
      // before saving the menu item.
      if (!isset($list['i18n_menu_node'])) {
        i18n_menu_node_menu_link_alter($item, NULL);
      }
      menu_link_save($item);
    }
  }
}

/**
 * Set as current menu item the source node to correctly handle the active
 * trail.
 */
function i18n_menu_node_set_item($node) {
  if (!empty($node->tnid) && $node->tnid != $node->nid && i18n_menu_node_menu_link_enabled($node)) {
    $item = menu_get_item();
    $href = explode('/', $item['href']);
    if ($href[0] == 'node') {
      menu_tree_set_path('main-menu', 'node/' . $node->tnid);
    }
  }
}

/**
 * Return TRUE if node item translation is enabled for the given menu.
 */
function i18n_menu_node_translation_enabled($menu_name) {
  return variable_get("i18n_menu_node_translation_{$menu_name}", TRUE);
}

/**
 * Return TRUE if node item translation is enabled for the given menu item.
 */
function i18n_menu_node_enabled($item) {
  return isset($item['options']['translations']);
}

/**
 * Return TRUE if the given node has a menu link with node translation enabled.
 */
function i18n_menu_node_menu_link_enabled($node) {
  static $items = array();
  $nid = empty($node->tnid) ? $node->nid : $node->tnid;
  if (!isset($items[$nid])) {
    $items[$nid] = FALSE;

    // Give priority to the default menu.
    $menu_name = variable_get('menu_default_node_menu', 'main-menu');
    $item = db_query_range("SELECT options FROM {menu_links} WHERE link_path = :nid AND menu_name = :mn AND module = 'menu' ORDER BY mlid ASC", 0, 1, array(
      ':nid' => "node/{$nid}",
      ':mn' => $menu_name,
    ))
      ->fetchAssoc();

    // Check all menus if a link does not exist in the default menu.
    if (!$item) {
      $item = db_query_range("SELECT options FROM {menu_links} WHERE link_path = :nid AND module = 'menu' ORDER BY mlid ASC", 0, 1, array(
        ':nid' => "node/{$nid}",
      ))
        ->fetchAssoc();
    }
    if ($item) {
      $item['options'] = unserialize($item['options']);
      $items[$nid] = i18n_menu_node_enabled($item);
    }
  }
  return $items[$nid];
}

/**
 * Return an array of node translation nids keyed by language.
 */
function i18n_menu_node_get_translations($tnid, $field = 'nid') {
  static $translations = array();
  if (!isset($translations[$tnid])) {
    $translations[$tnid] = array();
    if ($tnid) {
      $result = db_query("SELECT n.language, n.nid, n.status, n.type FROM {node} n WHERE n.tnid = :tnid", array(
        ':tnid' => $tnid,
      ));
      foreach ($result as $row) {
        $translations[$tnid][$row->language] = $row;
      }
    }
  }
  if (!$field) {
    return $translations[$tnid];
  }
  else {
    $result = array();
    foreach ($translations[$tnid] as $langcode => $node) {
      $result[$langcode] = $node->{$field};
    }
    return $result;
  }
}

/**
 * Remove the translation strings for the given menu item and language.
 */
function _i18n_menu_node_delete_item_translation($mlid, $langcode) {
  i18n_string_remove('menu:item:' . $mlid . ':title');
  i18n_string_remove('menu:item:' . $mlid . ':description');
}

Functions

Namesort descending Description
i18n_menu_node_block_info Implementation of hook_block_info().
i18n_menu_node_block_module_info Return the information used to provide the [i18n] blocks.
i18n_menu_node_block_view Implementation of hook_block_view().
i18n_menu_node_context_plugins Implementation of hook_context_plugins().
i18n_menu_node_context_registry_alter Implementation of hook_context_registry().
i18n_menu_node_enabled Return TRUE if node item translation is enabled for the given menu item.
i18n_menu_node_form_alter Implementation of hook_form_alter().
i18n_menu_node_form_i18n_node_select_translation_alter Implementation of hook_form_FORM_ID_alter().
i18n_menu_node_form_menu_edit_item_alter Implementation of hook_form_FORM_ID_alter().
i18n_menu_node_form_menu_edit_item_validate Validation handler for the menu item edit form.
i18n_menu_node_form_menu_edit_menu_alter Implementation of hook_form_FORM_ID_alter().
i18n_menu_node_form_node_submit Submit handler for the node form.
i18n_menu_node_get_translations Return an array of node translation nids keyed by language.
i18n_menu_node_init Implementation of hook_init().
i18n_menu_node_item_translations_refresh Refresh the translation cache for any menu item belonging to the translation sets identified by the given nids.
i18n_menu_node_item_translations_refresh_all Refresh the translation cache for any menu item pointing to a node page.
i18n_menu_node_item_translations_refresh_set Refresh the translation cache for any menu item belonging to the translation sets identified by the given tnid.
i18n_menu_node_menu_block_view Render the i18n version of a menu block.
i18n_menu_node_menu_edit_menu_submit Submit handler for the menu_edit_menu form.
i18n_menu_node_menu_link_alter Implementation of hook_menu_link_alter().
i18n_menu_node_menu_link_enabled Return TRUE if the given node has a menu link with node translation enabled.
i18n_menu_node_navigation_links_prepare Force the menu system to build its static cache without language selection.
i18n_menu_node_navigation_menu_block_view Render the navigation menu block.
i18n_menu_node_node_delete Implementation of hook_node_delete().
i18n_menu_node_node_insert Implementation of hook_node_insert().
i18n_menu_node_node_presave Implementation of hook_node_presave().
i18n_menu_node_node_update Implementation of hook_node_update().
i18n_menu_node_set_item Set as current menu item the source node to correctly handle the active trail.
i18n_menu_node_theme_registry_alter Implementation of hook_theme_registry_alter().
i18n_menu_node_tokens Implementation of hook_tokens().
i18n_menu_node_token_info Implementation of hook_token_info().
i18n_menu_node_translated_menu_link_alter Implementation of hook_translated_menu_link_alter().
i18n_menu_node_translation Return the nid of the node translation for the given language if available.
i18n_menu_node_translation_enabled Return TRUE if node item translation is enabled for the given menu.
i18n_menu_node_translation_set_update Submit handler for i18n_node_select_translation form.
_i18n_menu_node_check_path Check that the given item has a valid node path.
_i18n_menu_node_delete_item_translation Remove the translation strings for the given menu item and language.
_i18n_menu_node_get_item_title Optionally insert/update and return a localized menu item title.
_i18n_menu_node_get_node_tnid Return the translation set id from the given node.
_i18n_menu_node_get_tnid Return the translation set id for the give nid.
_i18n_menu_node_item_translations_refresh Helper function: actually refresh the menu items returned by the given query.

Constants

Namesort descending Description
I18N_MENU_NODE_BLOCK_SEPARATOR Every [i18n] block delta has the form <module><separator><delta>.