Hi,

I post this little howto here about a feature that we implemented for our site so that it can be found when googling or searching through drupal.org. Maybe someone else enjoys this solution when looking for sth. similar.

We are using the language sections modul (http://drupal.org/project/language_sections) for multilingual content. Thus, all our nodes are language neutral and are shown for all languages, so when we switch the language, the content changes although the node stays the same.

We are using our own menu for our nodes and we want the menu titles to change, too, when we switch the language, e.g. from "Teaching" to "Lehre" (german for teaching).

You need the i18n module (http://drupal.org/project/i18n) with i18nmenu, i18nblocks and i18nstrings enabled. Then you can enter translations for the menu item titles on the "Translate interface" page and i18nmenu will then localize the menu trees.

(Note: With the i18n version from today, Sep. 24th 2008, only menu items created on the menu build page are localized this way. Menu items created via the node edit page are not. We will fix this in the code below, too).

Now we wanted an easier way to translate the menu item. Since we write content for all languages in the same node edit form due to the language sections module, we wanted to enter the menu title translations on the same form, too. We are using our own module to implement this.

We are dealing only with two languages and enter the menu title always in german and need translations for english only. But the following code can easily be extended to offer a selection/drop-down box in the node edit form. That's up to you :-)

First we extend the node edit form:

function franks_module_form_menu(&$form, $form_state, $form_id) {
  // Make menu items added via the node edit form "customized", so that
  // i18nmenu localizes those menu items, too.
  $form['menu']['customized'] = array('#type' => 'value', '#value' => 1);

  // Try to fetch english translation of the menu title if it already exists.
  $item = $form['#node']->menu;
  if ($item['link_title']) {
    $link_title_translated =  tt('menu:item:'.$item['mlid'].':title', $item['link_title'], 'en', FALSE);
    if ($link_title_translated == $item['link_title']) {
      $link_title_translated = '';
    }
  }
  // Add box for english translation of the menu title and arrange it.
  $form['menu']['delete']['#weight'] = -2;
  $form['menu']['link_title']['#weight'] = -1;
  $form['menu']['link_title_translation'] = array('#type' => 'textfield',
    '#title' => 'English menu link title',
    '#weight' => 0,
    '#default_value' => $link_title_translated,
    '#required' => FALSE,
    );
}

Then we hook into nodeapi to store the menu link and its translation. The following code could be made more efficient, but its easy...

function franks_module_nodeapi(&$node, $op){
  
  switch($op) {
    case 'insert':
    case 'update':
      if (isset($node->menu)) {
        $item = $node->menu;
        
        // We don't need to store anything if there is no translation.
        if ($item['link_title_translation']) {
          // Save because it can be lost in menu_nodeapi.
          $translated = $item['link_title_translation'];

          // Newly created menu. We don't know the mlid yet.
          if (!$item['mlid']) {
            // Load the menu from the DB.
            $node->menu = NULL; // To force reloading in menu_nodeapi.
            menu_nodeapi($node, 'prepare');
            // Now we have the mlid.
            $item = $node->menu;
          }

          // $textgroup and $location form a context, e.g. "menu:item:1361:title"
          // Means that this string is the title of the menu item with mlid 1361.
          $textgroup = 'menu';
          $location = 'item:'.$item['mlid'].':title';
          
          $report = array(0, 0, 0); // Variable for storing reports. Required.
          // This function will insert and/or update source and translation and do all
          // the checks if they already exist etc. We don't need to care about all this.
          _locale_import_one_string_db($report,
                                       'en', // Which language is the translation for.
                                       $item['link_title'],
                                       $translated,
                                       $textgroup,  
                                       $location, 
                                       LOCALE_IMPORT_OVERWRITE, // Translation could have changed.
                                       NULL); // We don't need a plural here.
        }
      }
      break;
  }
}

Now you just enter the english translation in the node edit form right below the german menu link title, and after saving the node, its menu title will switch the language, too.

Hope this helps someone else :-)

cu,
Frank

Comments

marcvangend’s picture

Didn't you mean function franks_module_form_alter instead of function franks_module_form_menu?

Frank Steiner’s picture

Definitely :-)

adshill’s picture

Really useful, thanks! But tell me... have you found a way to do a similar thing with node titles? Because Language Sections is great for content, but we are still left with "one language" titles on our pages. This deals with menu items, but not with the title at the top of each page/discussion etc... Any help greatly appreciated!

Frank Steiner’s picture

I can't post a real code snippet because I removed all of this code in the meantime and use my own functions for translation where I just store all translated menu and page titles in a static array.

Nevertheless, the code for node titles is not really different. You need sth. like this in form_alter:

   if ($form['#node']->title_translated) {
      $title_translated = $form['#node']->title_translated;
    }
    else {
      if ($form['#node']->title) {
        $title = $form['#node']->title;
        $title_translated =  tt('node:'.$node->nid.':title', $form['#node']->title), 'en', FALSE);
        if ($title_translated == $form['#node']->title) {
          $title_translated = '';
        }
      }
    }
    // Add box for english translation of the page title and arrange it.
    $form['title_translated'] = array('#type' => 'textfield',
      '#title' => 'English title',
      '#weight' => -4,
      '#default_value' => $title_translated,
      '#required' => FALSE,
      );

In nodeapi use similar code for insert/update. I used textgroup='node' and location=$node->nid.':title''. The rest of the insert/update code is the same. Additionally you need sth. like this in nodeapi:

case 'view': 
   // Will keep the normal title if no translation exists.
   drupal_set_title(tt('node:'.$node->nid.':title', $form['#node']->title), 'en', FALSE);
   break;

This is untested but I guess you get the point :-)

adshill’s picture

Thanks a lot Frank... I'll give it a try...

krabbe’s picture

Subscribe