This module is part of i18n package and provides support for translation relationships.

The objects you can define translation relationships for are:

Module developed by Jose A. Reyero, www.reyero.net

' ); break; case 'admin/modules#description' : $output = t('Manages translations between nodes and taxonomy terms. Requires i18n module' ); break; case 'admin/access#translation': $output = t('

Translations

'); $output = t('translate nodes

This one, combined with create content permissions, will allow to create node translation

'); } 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); */ } else { 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); } // Change rewrite conditions when translating nodenode/add/type/translation/22/es if( arg(0) == 'node' && arg(1) == 'add' && arg(3) == 'translation' && ($lang = arg(5)) && array_key_exists($lang, i18n_supported_languages()) ) { i18n_selection_mode('translation', db_escape_string($lang)); } } 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); $form['i18n']['translation_nid'] = array('#type' => 'hidden', '#value' => $translation_nid); $form['i18n']['language']['#default_value'] = $language; translation_node_populate_fields($trans, $form, $node); if($trans->trid){ $form['i18n']['trid'] = array('#type' => 'hidden', '#value' => $trans->trid); } //print "
"; var_dump($form); print "
"; $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 Preview to get the right Categories & 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, $level = 0){ $fields = array(); foreach(element_children($form) as $key) { if($key == 'nid' || $key == 'vid' || $key == 'i18n'){ continue; } elseif(isset($source->$key) && !isset($node->$key)) { if($level == 0 && $key == 'parent' && is_numeric($source->parent)) { // Translate book outline $lang = $form['i18n']['language']['#default_value']; $trans = translation_node_get_translations(array('nid' => $source->parent)); if(isset($trans[$lang])) { $form['parent']['#default_value'] = $trans[$lang]->nid; } } else { $node->$key = $form[$key]['#default_value'] = $source->$key; $fields[] = $key; } } elseif(!isset($form[$key]['#tree'])) { translation_node_populate_fields($source, $form[$key], $node, $level +1); } } if($level = 0) { if($source->type == 'book' && $source->parent){ } } // For debugging // if($fields) drupal_set_message("Populated fields ($level): ". implode(', ', $fields)); } /** * 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]); break; case t('Save'): $output .= translation_node_form($node, $args[1]); break; 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"); default: $output .= translation_node_overview($node); } print theme('page', $output); } function translation_node_overview($node) { $languages = i18n_supported_languages(); unset($languages[$node->language]); $output = t('

Current translations

'); $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('reset'); 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("

No nodes available in %language

", 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')); } } /** * Hook nodeapi * * 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; case 'insert': 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(); $links = array(); 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); } } return $links; } /** * 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); $list = array(); while ($term = db_fetch_object($result)) { $list[$term->tid] = $term->name; } return $list; } /** * 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('reset'); $items = array(); while ($node = db_fetch_object($result)) { $items[$node->language] = $node; } return $items; } 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']; switch ($op) { case t('Save'): case 'edit': drupal_set_title(t('Edit term translations')); $output = translation_taxonomy_form($vid, arg(5), $edit); break; case t('Submit'): drupal_set_title(t('Submit')); translation_taxonomy_term_save($edit); $output = translation_taxonomy_overview($vid); break; 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\' AND a.trid != 0'; 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, i18n_path($url, $lang) , $lang); } return $links; } /** * 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, i18n_path('node/'.$node->nid, $baselang), $lang); } function theme_translation_link($text, $target, $lang, $separator=' ') { 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); } ?>