Skip to content
translation.module 21 KiB
Newer Older
<?php
// $Id$

/**
 * Internationalization (i18n) package.
 * 
 * Translation module: translation
 *
 * @author Jose A. Reyero, 2004, http://www.reyero.net
 *
 */

/**
 * Implementation of hook_help().
 */
function translation_help($section = 'admin/help#translation' ) {
  switch ($section) {
    case 'admin/help#translation' :
      $output = t('
        <p>This module is part of i18n package and provides support for translation relationships.</p>
        <p>The objects you can define translation relationships for are:</p>
        <ul>
        <li>Nodes</li>
        <li>Taxonomy Terms</li>
        </ul>
        <p><small>Module developed by Jose A. Reyero, <a href="http://www.reyero.net">www.reyero.net</a></small></p>' );
      break;
    case 'admin/modules#description' :
      $output = t('Manages translations between nodes and taxonomy terms. <b>Requires i18n module</b>' );
      break;
    case 'admin/access#translation':
      $output = t('<h2>Translations</h2>');
      $output = t('<strong>translate nodes</strong> <p>This one, combined with create content permissions, will allow to create node translation</p>');
  }
  return $output;
}

/**
 * Implementation of hook_menu().
 */
function translation_menu($may_cache) {
  $items = array();

  if ($may_cache) {
      $items[] = array(
        'path' => 'admin/node/translation',
        'title' => t('translation'),
        'callback' => 'translation_node_admin',
        'access' => user_access('administer nodes'),
        'type' => MENU_LOCAL_TASK);
    if (arg(0) == 'node' && is_numeric(arg(1)) && variable_get('i18n_node_'.translation_get_node_type(arg(1)), 0)) {
      $access = user_access('translate nodes');
      $type = MENU_LOCAL_TASK;      
      $items[] = array(
        'path' => 'node/'. arg(1) .'/translation', 
        'title' => t('translation'),
        'callback' => 'translation_node_page',
        'access' => $access,
        'type' => $type,
        'weight' => 3);
    }
    if(arg(0) == 'admin' && arg(1) == 'taxonomy' && is_numeric(arg(2)) ) {
      $items[] = array(
        'path' => 'admin/taxonomy/'.arg(2).'/translation', 
        'title' => t('translation'),
        'callback' => 'translation_taxonomy_admin',
        'access' => user_access('administer taxonomy'),
        'type' => MENU_LOCAL_TASK);
      
    }
  }

  return $items;
}

/**
 * Implementation of hook_perm
 */
function translation_perm(){
  return array('translate nodes');
}

/**
 * Implementation of hook_settings
 */
function translation_settings(){
  $form['i18n_translation_links'] = array(
    '#type' => 'radios',
    '#title' => t('Language Management'),
    '#default_value' => variable_get('i18n_translation_links', 0),
    '#options' => array(t('Interface language depends on content.'), t('Interface language is independent')),
    '#description' => t("How interface language and content language are managed."),
  );
  $form['i18n_translation_node_links'] = array(
    '#type' => 'radios',
    '#title' => t('Links to node translations'),
    '#default_value' => variable_get('i18n_translation_node_links', 0),
    '#options' => array(t('None.'), t('Main page only'), t('Teaser and Main page')),
    '#description' => t("How interface language and content language are managed."),
  );
  
  return $form;  
}
/**
 * Translation block
 * 
 * This is a simple language switcher which knows nothing about translations
 */
function translation_block($op = 'list', $delta = 0) {
  if ($op == 'list') {
    $blocks[0]['info'] = t('Translations');
  }
  elseif($op == 'view') {
    $blocks['subject'] = t('Languages');
    $blocks['content'] = theme('item_list', translation_get_links($_GET['q']));
  }

  return $blocks;
}

/**
 * Implementation of hook_form_alter
 */
function translation_form_alter($form_id, &$form) {
  //drupal_set_message("translation_form_alter form_id=$form_id ");
  // Node edit form
	if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id && variable_get('i18n_node_'.$form['type']['#value'], 0)){
    $node = $form['#node'];
    $languages = i18n_supported_languages();
    if ($node->nid && $node->translation) {
      $translations = $node->translation;
    }
    elseif (arg(3) == 'translation' && user_access('translate nodes')) {
      // We are translating a node: node/add/type/translation/nid/lang
      $translation_nid = arg(4);
      $language = arg(5);
      // Load the node to be translated and populate fields
      $trans = node_load($translation_nid);
      translation_node_populate_fields($trans, $form, $node);      
      $form['i18n']['translation_nid'] = array('#type' => 'hidden', '#value' => $translation_nid);
      $form['i18n']['language']['#default_value'] = $language;

      if($trans->trid){
        $form['i18n']['trid'] = array('#type' => 'hidden', '#value' => $trans->trid);
      }
      //print "<pre>"; var_dump($form); print "</pre>";          
      $translations = $trans->translation;
      $translations[$trans->language] = $trans;
    }
    //$form .= form_select(t('Language'), 'language', $node->language ? $node->language : i18n_get_lang(), $langselect, t('If you change the Language, you must click on <i>Preview</i> to get the right Categories &amp; Terms for that language.'));
    if ($translations) {
      // Unset invalid languages
      foreach(array_keys($translations) as $lang) {
          unset($form['i18n']['language']['#options'][$lang]);
      }
      // Add translation list
      $form['i18n']['#title'] = t('Language and translations');
      $form['i18n']['translations'] = array(
        '#type' => 'markup',
        '#value' => theme('translation_node_list', $translations, FALSE)
      );
    }
function translation_node_populate_fields($source, &$form, &$node){
    foreach(element_children($form) as $key) {
      if($key != 'nid' && $key != 'vid' && isset($source->$key) && !isset($node->$key)) {
        $node->$key = $form[$key]['#default_value'] = $source->$key;
        drupal_set_message("populated $key");
      } elseif(!isset($form[$key]['#tree'])) {
        translation_node_populate_fields($source, $form[$key], $node);
      }
    }
}
/**
 * Multilingual Nodes support
 */

/**
 * This is the callback for the tab 'translation' for nodes
 */
function translation_node_page() {
  $args = func_get_args();
  $op = isset($_POST['op']) ? $_POST['op'] : $args[0];
  $edit = $_POST['edit'];
  $nid = arg(1);
  $node = node_load($nid);
  drupal_set_title($node->title);
  $output = '';

  switch($op){
    case 'select':
      $output .= translation_node_overview($node);
      $output .= translation_node_form($node, $args[1]);
    case t('Save'):
      $output .= translation_node_form($node, $args[1]);
    case 'remove':
    case t('Remove'):
      db_query("UPDATE {i18n_node} SET trid = NULL WHERE nid=%d", $node->nid);
      drupal_set_message("The node has been removed from the translation set");
      drupal_goto("node/$node->nid/translation");
      $output .= translation_node_overview($node);
  }
  print theme('page', $output);  
function translation_node_overview($node) {
	  $languages = i18n_supported_languages();
	  unset($languages[$node->language]);
	  $output = t('<h2>Current translations</h2>');
	  $header = array(t('Language'), t('Title'), t('Options'));
	  foreach($languages as $lang => $langname){
	    $options = array();
	    if(isset($node->translation[$lang])){
	      $trnode = $node->translation[$lang];
	      $title = l($trnode->title, 'node/'.$trnode->nid);
	    } else {
	      $title = t('Not translated');
	      $options[] = l(t('create translation'), "node/add/$node->type/translation/$node->nid/$lang");
	    }
	    $options[] = l(t('select node'), "node/$node->nid/translation/select/$lang");
	    $rows[] = array($langname, $title, implode(" | ", $options));
	  }
	  $output .= theme('table', $header, $rows);
    if($node->trid){
      $form['submit'] = array('#type' => 'submit', '#value' => t('Remove'), '#suffix' => t('Remove node from this translation set'));
      $output .= drupal_get_form(NULL, $form);
    }
  return $output;
function translation_node_form($node, $lang){
	$form['node'] = array('#type' => 'value', '#value' =>$node);
  $form['language'] = array('#type' => 'hidden', '#value' => $lang);
  $form['source_nid'] = array('#type' => 'hidden', '#value' => $node->nid);
  $form['trid'] = array('#type' => 'hidden', '#value' => $node->trid);
  $languages = i18n_supported_languages();
  // Disable i18n rewrite. Order by trid to show first nodes with no translation
  i18n_selection_mode('off');
  $result = pager_query(db_rewrite_sql("SELECT n.nid, n.title FROM {node} n INNER JOIN {i18n_node} i ON n.nid = i.nid WHERE i.language = '%s' ORDER BY i.trid"), 40, 0, NULL, $lang);
  i18n_selection_mode('user');
  while($trnode = db_fetch_object($result)){
    $list[$trnode->nid] = l($trnode->title, "node/$trnode->nid") ;
  }
  if($list){
    $form['nodes']['nid'] = array(
    	'#type' => 'radios', '#title' => t('Select translation for %language', array('%language' => $languages[$lang])), 
    	'#default_value' => isset($node->translation[$lang]) ? $node->translation[$lang]->nid : '',
    	'#options' => $list);
    $form['pager'] = array('#value' => theme('pager'));
    $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
    return drupal_get_form('translation_node_form', $form);
  } else {
    return t("<p>No nodes available in %language</p>",  array('%language' => $languages[$lang]) );
function translation_node_form_submit($form_id, $form_values){
  $op = $_POST['op'];
  $source_nid = $form_values['source_nid'];
  $language = $form_values['language'];
  $nid = $form_values['nid'];
  if( $source_nid && $language && $nid ) {
   if($trid = $form_values['trid']){
      // Delete old translations
      db_query("UPDATE {i18n_node} SET trid = 0 WHERE trid = %d AND language = '%s'", $trid, $language);
    } else {
      $trid = db_next_id('{i18n_node}_trid');
    db_query("UPDATE {i18n_node} SET trid = %d WHERE nid=%d OR nid=%d", $trid, $source_nid, $nid); 
    drupal_set_message(t('The translation has been saved'));
  
  } 
 * 
 * Delete case is now handled in i18n_nodeapi
 */
function translation_nodeapi(&$node, $op, $arg = 0) {
  if (variable_get("i18n_node_$node->type", 0)) {
    switch ($op) {
      case 'load':
        $node->translation = translation_node_get_translations(array('nid' =>$node->nid), FALSE);
        break;
        if($node->translation_nid && !$node->trid){
          // Update both nodes
          $node->trid = db_next_id('{i18n_node}_trid');
          db_query("UPDATE {i18n_node} SET trid = %d WHERE nid=%d OR nid=%d", $node->trid, $node->nid, $node->translation_nid);
        }
        break;
    }
  }
}

/**
 * Taxonomy hook
 * $edit parameter is an array, not an object !!
 */
 // $op = insert, update, delete
function translation_taxonomy($op, $type, $edit = NULL) {
  switch ("$type/$op") {
    case 'term/insert':
    case 'term/update':
        if (!$edit['language'] && $edit['trid']) {
	        // Removed language, remove trid
	        db_query('UPDATE {term_data} SET trid=0 WHERE tid=%d', $edit['tid']);
	        if(db_affected_rows()) drupal_set_message(t('Removed translation information from term'));
	      }
        break;
  }
} 
/**
 * Implementation of hook_link().
 */
function translation_link($type, $node = NULL, $teaser = FALSE) {
  $languages = i18n_supported_languages();
  if ($type == 'node' && variable_get('i18n_translation_node_links', 0) > ($teaser ? 1 : 0) && $node->translation) {
    foreach ($node->translation as $lang => $trnode) {
        $baselang = variable_get('i18n_translation_links', 0) ? i18n_get_lang() : $lang;
        $links[]= theme('translation_node_link', $trnode , $lang, $baselang);
 * Returns a list for terms for vocabulary, language
function translation_vocabulary_get_terms($vid, $lang, $status = 'all') {
  switch($status){
    case 'translated':
      $andsql = ' AND trid > 0';
      break;
    case 'untranslated':
      $andsql = ' AND trid = 0';
      break;
    default:
      $andsql = '';
  $result = db_query("SELECT * FROM {term_data} WHERE vid=%d AND language='%s' $andsql", $vid, $lang);
  while ($term = db_fetch_object($result)) {
     $list[$term->tid] = $term->name;
	*	Get translations
	* @param $params = array of parameters
	* @param $getall = TRUE to get the also node itself
function translation_node_get_translations($params, $getall = TRUE) {
  foreach($params as $field => $value) {
    $conds[] = "b.$field = '%s'";
    $values[] = $value;
  if(!$getall){ // If not all, a parameter must be nid
    $conds[] = "n.nid != %d";
    $values[] = $params['nid'];
  $conds[] = "b.trid != 0";
  $sql = 'SELECT n.nid, n.title, n.status, a.language FROM {node} n INNER JOIN {i18n_node} a ON n.nid = a.nid INNER JOIN {i18n_node} b ON a.trid = b.trid WHERE '. implode(' AND ', $conds);

  i18n_selection_mode('off');
  $result = db_query(db_rewrite_sql($sql), $values);
  i18n_selection_mode('user');
    
  $items = array();
  while ($node = db_fetch_object($result)) {
    $items[$node->language] = $node;
function translation_get_node_type($nid) {
  return db_result(db_query('SELECT type FROM {node} WHERE nid=%d', $nid));
}
/**
 * Multilingual Taxonomy
 *
 */
 
/**
 * This is the callback for taxonomy translations
 * 
 * Gets the urls:
 * 		admin/taxonomy/i18n/term/xx
 * 		admin/taxonomy/i18n/term/new/xx
 * 		admin/taxonomy/vid/translation/op/trid
function translation_taxonomy_admin() {
  $vid = arg(2);
  $op = $_POST['op'] ? $_POST['op'] : arg(4);
  $edit = $_POST['edit'];
    case t('Save'):    
    case 'edit':
      drupal_set_title(t('Edit term translations'));
      $output = translation_taxonomy_form($vid, arg(5), $edit);
    case t('Submit'):
      drupal_set_title(t('Submit'));
      translation_taxonomy_term_save($edit);
      $output = translation_taxonomy_overview($vid);
    case 'delete':
      //print theme('page', node_delete($edit), t('Delete'));
      break;
    default:
      $output = translation_taxonomy_overview($vid);
  }
  return $output;   
}  
    
/**
 * Generate a tabular listing of translations for vocabularies.
 */

function translation_taxonomy_overview($vid) {
  $vocabulary = taxonomy_get_vocabulary($vid);
  drupal_set_title(check_plain($vocabulary->name));
 
  $languages = i18n_supported_languages();
  $header = array_merge($languages, array(t('Operations')));
  $links = array();
  $types = array();
	// Get terms/translations for this vocab 
	$result = db_query('SELECT * FROM {term_data} t WHERE vid=%d',$vocabulary->vid);
	$terms = array();
	while ($term = db_fetch_object($result)) {
	  if($term->trid && $term->language) {
	    $terms[$term->trid][$term->language] = $term;
	  }
	}
	// Reorder data for rows and languages
  foreach ($terms as $trid => $terms) {
    $thisrow = array();
    foreach ($languages as $lang => $name) {
      if (array_key_exists($lang, $terms)) {
        $thisrow[] = $terms[$lang]->name;
       }
       else {
        $thisrow[] = '--';
       }
    $thisrow[] = l(t('edit'), "admin/taxonomy/$vid/translation/edit/$trid");
    $rows[] = $thisrow;
  $output .= theme('table', $header, $rows);
  $output .= l(t('new translation'), "admin/taxonomy/$vid/translation/edit/new");
  return $output;

function translation_taxonomy_form($vid, $trid = NULL, $edit = array()) {
  $languages = i18n_supported_languages();
  if ($trid == 'new') {
    $translations = array();
  } else {
    $form['trid'] = array('#type' => 'hidden', '#value' => $trid);
    $translations = translation_term_get_translations(array('trid' =>$trid));
  //var_dump($translations);
  $vocabulary = taxonomy_get_vocabulary($vid);
  
  // List of terms for languages
  foreach ($languages as $lang => $langname) {
    $current = isset($translations[$lang]) ? $translations[$lang]->tid : '';
    $list = translation_vocabulary_get_terms($vid, $lang, 'all');
    $list[''] = '--';
    $form[$lang] = array('#type' => 'fieldset', '#tree' => TRUE);
    $form[$lang]['tid'] = array(
      '#type' => 'select', 
      '#title' => $langname,
    	'#default_value' => $current,
    	'#options' => $list
    );
    $form[$lang]['old'] = array('#type' => 'hidden', '#value' =>$current);
  }

  $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
  return drupal_get_form('translation_taxonomy', $form);
function translation_taxonomy_submit($form_id, $form_values) {
  $trid = $form_values['trid'];
  $languages = i18n_supported_languages();
  $translations = array();
  // Delete old translations
  if($trid){
    db_query("UPDATE {term_data} SET trid = 0 WHERE trid=%d", $trid);
  }
  foreach ($languages as $lang => $name) {
    if (is_numeric($form_values[$lang]['tid'])) {
      $translations[$lang] = $form_values[$lang]['tid'];
    }
  }
  if(count($translations)) {
    $trid = is_numeric($trid) ? $trid : db_next_id('{term_data}_trid');
    db_query('UPDATE {term_data} SET trid=%d WHERE tid IN(%s)', $trid, implode(',',$translations));
  }
  drupal_set_message(t('Term translations have been updated'));
}

/**
 * Converts a list of arrays to an array of the form keyfield => namefield
 */
function translation_array2list($data, $keyfield, $namefield = 'name') {
  foreach ($data as $key => $value) {
    if (is_array($data)) {
      $list[$value[$keyfield]] = $value[$namefield];
    }
    else {
      $list[$value->$keyfield] = $value->$namefield;
    }
  }
  return $list;
}

// returns array lang > term
function translation_term_get_translations($params, $getall = TRUE) {
  foreach($params as $field => $value) {
    $conds[] = "i.$field = '%s'";
    $values[] = $value;
  }
  if(!$getall){ // If not all, a parameter must be tid
    $conds[] = "t.tid != %d";
    $values[] = $params['tid'];
  }  
  $sql = 'SELECT t.* FROM {term_data} t INNER JOIN {term_data} i ON t.trid = i.trid WHERE '. implode(' AND ', $conds);;
  $result = db_query($sql, $values);
  $items = array();
  while ($data = db_fetch_object($result)) {
    $items[$data->language] = $data;
  return $items;  
}

function translation_url($url, $lang) {
  global $i18n_langpath;
  // If !url get from original request
  if (!$url) {
    $url = _i18n_get_original_path();
  }
  // If url has lang_prefix, remove it
  i18n_get_lang_prefix($url, true);
  // are we looking at a node?
  if (preg_match("/^(node\/)([0-9]*)$/",$url,$matches)) {
    if ($nid = translation_node_nid($matches[2], $lang)) {
      $url = "node/$nid";
    }
  }
  // or a taxonomy term
  elseif (preg_match("/^(taxonomy\/term\/)([^\/]*)$/",$url,$matches)) {//or at a taxonomy-listing?
    if ($str_tids = translation_taxonomy_tids($matches[2], $lang)) {
      $url = "taxonomy/term/$str_tids";
    }
  }
  
  return $url;
}

/**
 *  Returns an url-part, pointing to the translated node, if exists 
 */
function translation_node_nid($nid, $lang) {
  $sql = 'SELECT n.nid FROM {i18n_node} n INNER JOIN {i18n_node} a ON n.nid = a.nid INNER JOIN {i18n_node} b ON a.trid = b.trid AND b.nid =%d WHERE n.nid != %d AND n.language = \'%s\'';
  return db_result(db_query($sql, $nid, $nid, $lang));
}
/**
 *  Returns an url for the translated taxonomy-page, if exists 
 */
function translation_taxonomy_tids($str_tids, $lang) {
  if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str_tids)) {
    $separator = '+';
    // The '+' character in a query string may be parsed as ' '.
    $tids = preg_split('/[+ ]/', $str_tids);
  }
  else if (preg_match('/^([0-9]+,)*[0-9]+$/', $str_tids)) {
    $separator = ',';
    $tids = explode(',', $str_tids);
  }
  else {
    return;
  }
  $translated_tids = array();
  foreach ($tids as $tid) {
    if ($translated_tid = translation_term_get_translations(array('tid' =>$tid))) {
      $translated_tids[] = $translated_tid[$lang]->tid;
    }
  }
  return implode($separator, $translated_tids);
}
 * function translation_get_links
 * Returns an array of links for all languages, with or without names
 */
function translation_get_links($path = '', $names = 1) {
  $current = i18n_get_lang();
  foreach(i18n_supported_languages() as $lang => $name){
    $url = translation_url($path, $lang);
    $name = $names ? $name: '' ; // Should be localized??
    $links[]= theme('i18n_link', $name, "$lang/$url" , $lang);
/**
 * Themeable functions
 */
function theme_translation_node_link($node, $lang, $baselang = NULL, $title = FALSE){
  $baselang = $baselang ? $baselang : $lang;
  if($title){
    $name = $node->title;
  } else {
    $languages = i18n_supported_languages();
    $name = $languages[$lang];
  }
  return theme('i18n_link', $name, "$baselang/node/$node->nid", $lang);
}

function theme_translation_link($text, $target, $lang, $separator='&nbsp;') {
  return theme('i18n_link', $text, $target, $lang, $separator);
}

function theme_translation_node_list($list){
  $header = array(t('Language'), t('Title'));
  $languages = i18n_supported_languages();
  foreach($list as $lang => $node){
    $rows[] = array($languages[$lang], l($node->title, 'node/'.$node->nid) );
  }
  return theme('table', $header, $rows);
}