diff --git a/INSTALL.txt b/INSTALL.txt index bc3b0b3ce3e81378b7868db7dd4ba84ca1075236..c2dc67a68bdfea80ac1332cc8b37f7cd555a8554 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -18,6 +18,8 @@ INSTALLATION: 1. Create folder 'modules/i18n', and copy all the modules files, keeping directory structure, to this folder. 2. If updating, run the update.php script following the standard procedure for Drupal updates. +2. Make sure to enable the locale, path, and page modules. +3. If updating, run the update.php script following the standard procedure for Drupal updates. POST-INSTALLATION/CONFIGURATION: ============ diff --git a/contrib/i18nblocks.info b/contrib/i18nblocks.info new file mode 100644 index 0000000000000000000000000000000000000000..1fd3c36940f4900739a64b14e2c9c7012aea69dd --- /dev/null +++ b/contrib/i18nblocks.info @@ -0,0 +1,5 @@ +name = i18n - blocks +description = Enables multilingual blocks. +dependencies = i18n +package = Multilanguage - i18n +version = 5.0 dev \ No newline at end of file diff --git a/contrib/i18nblocks.module b/contrib/i18nblocks.module index dc9ffe13bb43e4900c90e96f5207d478edee68a3..cacc0f0aba6aa6dc2e02093b03c5a57ae1920dff 100644 --- a/contrib/i18nblocks.module +++ b/contrib/i18nblocks.module @@ -17,10 +17,8 @@ function i18nblocks_help($section = 'admin/help#i18nblocks' ) {

These are not real blocks, but metablocks that group together a number of normal blocks and display the right one depending on language

In the block administration pages you will find a new tab for creating "Multilingual blocks". Set them up as usual (region, visibility, etc...) and then click on configuration, to define which one of the other blocks will be shown for each language.

' ); - case 'admin/block/i18n': + case 'admin/build/block/i18n': return t('

These are not real blocks, but metablocks that group together a number of normal blocks and display the right one depending on language

'); - case 'admin/modules#description' : - return t('Manages multilingual meta-blocks. Requires i18n and locale module' ); } } @@ -30,7 +28,7 @@ function i18nblocks_help($section = 'admin/help#i18nblocks' ) { function i18nblocks_menu($may_cache) { $items = array(); if($may_cache){ - $items[] = array('path' => 'admin/block/i18n', 'title' => t('add multilingual block'), + $items[] = array('path' => 'admin/build/block/i18n', 'title' => t('add multilingual block'), 'access' => user_access('administer blocks'), 'callback' => 'i18nblocks_add', 'type' => MENU_LOCAL_TASK); @@ -82,7 +80,7 @@ function i18nblocks_form($i18nblock){ } } $form['name'] = array('#type' => 'textfield', '#title' => t('Block description'), - '#default_value' => isset($i18nblock['name']) ? $i18nblock['name'] : t('Multilingual block %number', array('%number' => $i)), + '#default_value' => !is_array($i18nblock['name']) ? $i18nblock['name'] : t('Multilingual block !number', array('!number' => variable_get('i18nblocks_number', 0) +1)), '#size' => 40, '#maxlength' => 40 ); @@ -95,6 +93,10 @@ function i18nblocks_form($i18nblock){ '#options' => $blocklist ); } + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Save'), + ); return $form; } /** @@ -104,10 +106,6 @@ function i18nblocks_add(){ $delta = variable_get('i18nblocks_number', 0) +1; $block['name'] = t('Multilingual block ').$delta; $form = i18nblocks_form($block); - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Save'), - ); $output = drupal_get_form('i18nblocks_form', $form); print theme('page', $output); } @@ -117,7 +115,7 @@ function i18nblocks_form_submit($form_id, $form_values){ variable_set('i18nblocks_number', $delta); i18nblocks_save($form_values, $delta); drupal_set_message(t('The new multilingual block has been created.')); - drupal_goto('admin/block'); + drupal_goto('admin/build/block'); } /** * Db layer: for now it stores each block as a variable @@ -135,4 +133,3 @@ function i18nblocks_save($edit, $delta){ } variable_set('i18nblocks_'.$delta, $block); } -?> \ No newline at end of file diff --git a/contrib/i18nmenu.info b/contrib/i18nmenu.info new file mode 100644 index 0000000000000000000000000000000000000000..65fd46754ba20bf59b256305a19b8b24a6cdabba --- /dev/null +++ b/contrib/i18nmenu.info @@ -0,0 +1,5 @@ +name = i18n - menu +description = Supports translatable custom menu items. +dependencies = i18n +package = Multilanguage - i18n +version = 5.0 dev diff --git a/contrib/i18nmenu.module b/contrib/i18nmenu.module index ed155a9d6f0fe5ee842b96dce9df1b8f2d2ab43a..f6d03cb91ed9969da9829612ab09acfd33e28d3d 100644 --- a/contrib/i18nmenu.module +++ b/contrib/i18nmenu.module @@ -26,7 +26,7 @@ function i18nmenu_translate_all(){ global $user; global $locale; $cid = "menu:$user->uid:$locale"; - cache_clear_all($cid); + cache_clear_all($cid, 'cache_menu'); // Translate all user defined meny items foreach($_menu['items'] as $mid => $item) { if($item['type'] & MENU_CREATED_BY_ADMIN) { @@ -34,7 +34,7 @@ function i18nmenu_translate_all(){ } } // Update cache - cache_set($cid, serialize($_menu), time() + (60 * 60 * 24)); + cache_set($cid, 'cache_menu', serialize($_menu), time() + (60 * 60 * 24)); } function i18nmenu_help($section = 'admin/help#i18nmenu' ) { @@ -48,10 +48,7 @@ function i18nmenu_help($section = 'admin/help#i18nmenu' ) {
  • Switch language to some non english one -while viewing the block-, so the locales table is populated with the new strings
  • Use the localization system to translate menu item strings
  • ' ); - case 'admin/modules#description' : - return t('Supports translatable custom menu items. Requires i18n and locale module' ); } return $output; } -?> \ No newline at end of file diff --git a/i18n.inc b/i18n.inc index 32acfdc9973914f116c952b7a9a603a253acdcdf..947d83cba076db4b6b93f5db63a916a5b833c1cb 100644 --- a/i18n.inc +++ b/i18n.inc @@ -108,7 +108,7 @@ function i18n_exit(){ * - translation module may reduce language selection options in case there already exist translations */ function i18n_form_alter($form_id, &$form) { - //drupal_set_message("i18n_form_alter form_id=$form_id "); + // drupal_set_message("i18n_form_alter form_id=$form_id "); switch($form_id){ case 'taxonomy_overview_vocabularies': $vocabularies = taxonomy_get_vocabularies(); @@ -133,19 +133,20 @@ function i18n_form_alter($form_id, &$form) { default: // Content type settings - if (isset($form['type']) && $form['type']['#value'] .'_node_settings' == $form_id) { - $form['workflow']['i18n_node_'. $form['type']['#value']] = array( + if ($form_id == 'node_type_form') { + $node_type = $form['old_type']['#value']; + $form['workflow']['i18n_node'] = array( '#type' => 'radios', '#title' => t('Multilingual support'), - '#default_value' => variable_get('i18n_node_'. $form['type']['#value'], 0), + '#default_value' => variable_get('i18n_node_'.$node_type, 0), '#options' => array(t('Disabled'), t('Enabled')), '#description' => t('Enables language field and multilingual support for this content type.'), ); - } + } // Node edit form - if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id){ + if ($form_id == 'page_node_form') { // Language field - if(variable_get('i18n_node_'.$form['type']['#value'], 0) && !isset($form['i18n']['language'])) { + if(variable_get('i18n_node_page', 0) && !isset($form['i18n']['language'])) { // Language field $form['i18n'] = array('#type' => 'fieldset', '#title' => t('Language'), '#collapsible' => TRUE, '#collapsed' => FALSE, '#weight' => -4); // Language will default to current only when creating a node @@ -283,7 +284,7 @@ function _i18n_variable_init(){ while ($variable = db_fetch_object($result)) { $variables[$variable->name] = unserialize($variable->value); } - cache_set('variables:'.$lang, serialize($variables)); + cache_set('variables:'.$lang, 'cache', serialize($variables)); } return $variables; @@ -306,8 +307,7 @@ function _i18n_variable_exit(){ } } if($refresh) { - cache_set('variables:'.$lang, serialize($i18n_conf)); + cache_set('variables:'.$lang, 'cache', serialize($i18n_conf)); } } } -?> \ No newline at end of file diff --git a/i18n.info b/i18n.info new file mode 100644 index 0000000000000000000000000000000000000000..f402c4af181842be73550c972d359c93ba5c3313 --- /dev/null +++ b/i18n.info @@ -0,0 +1,5 @@ +name = Internationalization +description = Enables multilingual content. +dependencies = locale +package = Multilanguage - i18n +version = 5.0 dev \ No newline at end of file diff --git a/i18n.module b/i18n.module index 67f3b16b00ca5716bd4a17fce72f24d289df6551..16cb0f37afeab24c059b424a0fdf9c6865ac1eb9 100644 --- a/i18n.module +++ b/i18n.module @@ -71,9 +71,9 @@ function i18n_init(){ $_GET['q'] = i18n_get_normal_path($path); } - // If not in bootstrap, include hooks + // If not in bootstrap, variable init if(!_i18n_is_bootstrap()){ - include drupal_get_path('module', 'i18n').'/i18n.inc'; + //include drupal_get_path('module', 'i18n').'/i18n.inc'; i18n_variable_init(); } } @@ -96,11 +96,16 @@ function i18n_help($section = 'admin/help#i18n' ) {
  • Support for long locale names
  • Module developed by Jose A. Reyero, www.reyero.net

    ' ); - case 'admin/modules#description' : - return t('Enables multilingual content. Requires locale module for interface translation' ); } } +/** + * Implementation of hook_perm + */ +function i18n_perm(){ + return array('administer i18n'); +} + /** * Implementation of hook_menu(). * Modify rewriting conditions when viewing specific nodes @@ -108,6 +113,13 @@ function i18n_help($section = 'admin/help#i18n' ) { function i18n_menu($may_cache) { $items = array(); if (!$may_cache) { + $items[] = array( + 'path' => 'admin/settings/i18n', + 'title' => t('i18n settings'), + 'callback' => 'i18n_settings', + 'access' => user_access('administer i18n'), + 'type' => MENU_NORMAL_ITEM, + ); if (arg(0) == 'node') { if(isset($_POST['edit']['language']) && $_POST['edit']['language']) { $language = $_POST['edit']['language']; @@ -125,13 +137,20 @@ function i18n_menu($may_cache) { return $items; } /** - * Implementation of hook_settings(). + * Menu callback. * * Some options have been removed from previous versions: * - Languages are now taken from locale module unless defined in settings file * - Language dependent tables are authomatically used if defined in settings file */ function i18n_settings() { + return drupal_get_form('i18n_settings_form'); +} + +/** + * Form builder function + */ +function i18n_settings_form() { $form['i18n_browser'] = array( '#type' => 'radios', @@ -180,7 +199,7 @@ function i18n_settings() { '#description' => t('Determines which content to show depending on language.'), ); - return $form; + return system_settings_form($form); } /** @@ -493,4 +512,304 @@ function theme_i18n_language_icon($lang){ } /* @} */ -?> \ No newline at end of file + +/** + * Implementation of conf_url_rewrite + * + * This is a conditional definition, just in case it is defined somewhere else. + * If so, path rewriting won't work properly but at least it won't break Drupal + */ + +if(!function_exists('custom_url_rewrite')) { + function custom_url_rewrite($type, $path, $original) { + return i18n_url_rewrite($type, $path, $original); + } +} + +function i18n_url_rewrite($type, $path, $original){ + //drupal_set_message("type=$type path=$path original=$original"); + if ($type == 'alias' && !i18n_get_lang_prefix($path) ){ + return $path ? i18n_get_lang() . '/'. $path : i18n_get_lang(); + } else { + return $path; + } +} + +/** + * Implementation of hook_db_rewrite_sql() + */ +function i18n_db_rewrite_sql($query, $primary_table, $primary_key){ + // Some exceptions for query rewrites + $mode = i18n_selection_mode(); + // drupal_set_message("i18n_db_rewrite mode=$mode query=$query"); + if($mode == 'off') return; + + switch ($primary_table) { + case 'n': + case 'node': + // Node queries + return i18n_db_node_rewrite($query, $primary_table, $primary_key, $mode); + case 't': + case 'v': + // Taxonomy queries + return i18n_db_taxonomy_rewrite($query, $primary_table, $primary_key, $mode); + } +} + +function i18n_db_node_rewrite($query, $primary_table, $primary_key, $mode){ + // When loading specific nodes, language conditions shouldn't apply + // TO-DO: Refine this regexp + if (preg_match("/WHERE.*$primary_table.nid\s*=\s*(\d|%d)/", $query)) return; + + $result['join'] = "LEFT JOIN {i18n_node} i18n ON $primary_table.nid = i18n.nid"; + $result['where'] = i18n_db_rewrite_where('i18n', $mode); + + return $result; +} + +function i18n_db_taxonomy_rewrite($query, $primary_table, $primary_key, $mode){ + // When loading specific terms, vocabs, language conditions shouldn't apply + // TO-DO: Refine this regexp + if (preg_match("/WHERE.* $primary_table\.tid\s*(=\s*\d|IN)/", $query)) return; + + $result['where'] = i18n_db_rewrite_where($primary_table, $mode); + + return $result; +} + +function i18n_db_rewrite_where($alias, $mode){ + switch($mode){ + case 'simple': + return "$alias.language ='".i18n_get_lang()."' OR $alias.language ='' OR $alias.language IS NULL" ; + case 'mixed': + return "$alias.language ='".i18n_get_lang()."' OR $alias.language ='".i18n_default_language()."' OR $alias.language ='' OR $alias.language IS NULL" ; + case 'strict': + return "$alias.language ='".i18n_get_lang()."'" ; + case 'node': + case 'translation': + return "$alias.language ='".i18n_selection_mode('params')."' OR $alias.language ='' OR $alias.language IS NULL" ; + case 'default': + return "$alias.language ='".i18n_default_language()."' OR $alias.language ='' OR $alias.language IS NULL" ; + case 'custom': + return str_replace('%alias',$alias, i18n_selection_mode('params')); + } +} + +/** + * Implementation of hook_exit + */ +function i18n_exit(){ + _i18n_variable_exit(); +} + +/** + * Implementation of hook_form_alter + * + * This is the place to add language fields to all forms + * Alan: - changed to test in case translation_form_alter (or another module/mechanism) has already set language + * - translation module may reduce language selection options in case there already exist translations + */ +function i18n_form_alter($form_id, &$form) { + //drupal_set_message("i18n_form_alter form_id=$form_id "); + switch($form_id){ + case 'taxonomy_overview_vocabularies': + $vocabularies = taxonomy_get_vocabularies(); + $languages = i18n_supported_languages(); + foreach ($vocabularies as $vocabulary) { + if($vocabulary->language) $form[$vocabulary->vid]['type']['#value'] = $form[$vocabulary->vid]['type']['#value'].' ('.$languages[$vocabulary->language].')'; + } + break; + case 'taxonomy_form_vocabulary': // Taxonomy vocabulary + if(isset($form['vid'])) { + $vocabulary = taxonomy_get_vocabulary($form['vid']['#value']); + } + $form['language'] = _i18n_language_select(isset($vocabulary) ? $vocabulary->language : i18n_get_lang(),t('This language will be set for all terms in this vocabulary')); + break; + + case 'taxonomy_form_term': // Taxonomy term + if(isset($form['tid']) && is_numeric($form['tid']['#value'])) { + $term = taxonomy_get_term($form['tid']['#value']); + } + $form['language'] = _i18n_language_select(isset($term) ? $term->language : i18n_get_lang()); + break; + + case 'node_type_form': + $node_type = $form['old_type']['#value']; + $form['workflow']['i18n_node'] = array( + '#type' => 'radios', + '#title' => t('Multilingual support'), + '#default_value' => variable_get('i18n_node_'.$node_type, 0), + '#options' => array(t('Disabled'), t('Enabled')), + '#description' => t('Enables language field and multilingual support for this content type.'), + ); + break; + + default: + // Node edit form + if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) { + // Language field + if(variable_get('i18n_node_'.$form['type']['#value'], 0) && !isset($form['i18n']['language'])) { + // Language field + $form['i18n'] = array('#type' => 'fieldset', '#title' => t('Multilingual settings'), '#collapsible' => TRUE, '#collapsed' => FALSE, '#weight' => -4); + // Language will default to current only when creating a node + $language = isset($form['#node']->language) ? $form['#node']->language : (arg(1)=='add' ? i18n_get_lang() : ''); + $form['i18n']['language'] = _i18n_language_select($language, t('If you change the Language, you must click on Preview to get the right Categories & Terms for that language.'), -4); + } + // Correction for lang/node/nid aliases generated by path module + // if($form['#node']->path && $form['#node']->path == i18n_get_lang().'/node/'.$form['#node']->nid){ + if($form['#node']->path) { + $alias = drupal_lookup_path('alias', 'node/'.$form['#node']->nid); + if($alias && $alias != 'node/'.$form['#node']->nid){ + $form['#node']->path = $alias; + } else { + unset($form['#node']->path); + } + } + // Some language values for node forms + // To-do: addapt for translations too + /* + if($language && $form['#node']->type == 'book') { + i18n_selection_mode('custom', "%alias.language ='$language' OR %alias.language IS NULL" ); + $form['parent']['#options'] = book_toc($form['#node']->nid); + i18n_selection_mode('reset'); + } + */ + + } + + } +} + +/** + * Implementation of hook_nodeapi + * Updated for new table i18n_node + */ +function i18n_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { + if (variable_get("i18n_node_$node->type", 0)) { + switch ($op) { + case 'load': + return db_fetch_array(db_query("SELECT trid, language, status AS i18n_status FROM {i18n_node} WHERE nid=%d", $node->nid)); + case 'insert': + case 'update': + db_query("DELETE FROM {i18n_node} WHERE nid=%d",$node->nid); + if($node->language){ + db_query("INSERT INTO {i18n_node} (nid, trid, language, status) VALUES(%d, '%d', '%s', '%d')", $node->nid, $node->trid, $node->language, $node->i18n_status); + } + break; + case 'delete': + db_query('DELETE FROM {i18n_node} WHERE nid=%d', $node->nid); + break; + } + } +} + +/** + * Helper function to create language selector + */ +function _i18n_language_select($value ='', $description ='', $weight = -20){ + return array( + '#type' => 'select', + '#title' => t('Language'), + '#default_value' => $value, + '#options' => array_merge(array('' => ''), i18n_supported_languages()), + '#description' => $description, + '#weight' => $weight, + ); +} + +/** + * Implementation of hook_taxonomy + * + * $edit parameter may be an array or an object !! + */ +function i18n_taxonomy($op, $type, $edit = NULL) { + $edit = (array)$edit; + switch ("$type/$op") { + case 'term/insert': + case 'term/update': + $language = isset($edit['language']) ? $edit['language'] : ''; + db_query("UPDATE {term_data} SET language='%s' WHERE tid=%d", $language, $edit['tid']); + break; + case 'vocabulary/insert': + case 'vocabulary/update': + $language = isset($edit['language']) ? $edit['language'] : ''; + db_query("UPDATE {vocabulary} SET language='%s' WHERE vid=%d", $language, $edit['vid']); + if ($language && $op == 'update') { + db_query("UPDATE {term_data} t SET t.language='%s' WHERE t.vid=%d", $edit['language'], $edit['vid']); + drupal_set_message(t('Reset language for all terms.')); + } + break; + } +} + +/** + * Language block + * + * This is a simple language switcher which knows nothing about translations + */ +function i18n_block($op = 'list', $delta = 0) { + if ($op == 'list') { + $blocks[0]['info'] = t('Language switcher'); + } + elseif($op == 'view') { + $blocks['subject'] = t('Languages'); + $blocks['content'] = theme('item_list', i18n_get_links($_GET['q'])); + } + return $blocks; +} + +/** + * Multilingual variables + */ +function i18n_variable_init(){ + global $conf; + global $i18n_conf; + $lang = _i18n_get_lang(); + if($i18n_variables = variable_get('i18n_variables', '')){ + $i18n_conf = array(); + $variables = _i18n_variable_init(); + foreach($i18n_variables as $name){ + $i18n_conf[$name] = isset($variables[$name]) ? $variables[$name] : (isset($conf[$name]) ? $conf[$name] : ''); + } + $conf = array_merge($conf, $i18n_conf); + } +} + +function _i18n_variable_init(){ + $lang = _i18n_get_lang(); + $variables = array(); + if ($cached = cache_get('variables:'.$lang)) { + $variables = unserialize($cached->data); + } + else { + $result = db_query("SELECT * FROM {i18n_variable} WHERE language='%s'", $lang); + while ($variable = db_fetch_object($result)) { + $variables[$variable->name] = unserialize($variable->value); + } + cache_set('variables:'.$lang, 'cache', serialize($variables)); + } + + return $variables; + +} + +function _i18n_variable_exit(){ + global $i18n_conf; + global $conf; + if($i18n_conf){ + $lang = _i18n_get_lang(); + $refresh = FALSE; + // Rewritten because array_diff_assoc may fail with array variables + foreach($i18n_conf as $name => $value){ + if($value != $conf[$name]) { + $refresh = TRUE; + $i18n_conf[$name] = $conf[$name]; + db_query("DELETE FROM {i18n_variable} WHERE name='%s' AND language='%s'", $name, $lang ); + db_query("INSERT INTO {i18n_variable} (language, name, value) VALUES('%s', '%s', '%s')", $lang, $name, serialize($conf[$name])); + } + } + if($refresh) { + cache_set('variables:'.$lang, 'cache', serialize($i18n_conf)); + } + } +} diff --git a/i18nprofile/i18nprofile.info b/i18nprofile/i18nprofile.info new file mode 100644 index 0000000000000000000000000000000000000000..12245d044442594428c6f269b944858d3e5037e9 --- /dev/null +++ b/i18nprofile/i18nprofile.info @@ -0,0 +1,5 @@ +name = i18n - profile +description = Enables multilingual profile fields. +dependencies = i18n profile +package = Multilanguage - i18n +version = 5.0 dev diff --git a/translation.info b/translation.info new file mode 100644 index 0000000000000000000000000000000000000000..8fbda1d2e51dc8bf2fa092a77342b3010f269b1b --- /dev/null +++ b/translation.info @@ -0,0 +1,5 @@ +name = Translation +description = Manages translations between nodes and taxonomy terms. +dependencies = i18n +package = Multilanguage - i18n +version = 5.0 dev diff --git a/translation.module b/translation.module index 9c6c3b4f3963bed640fa8f0aea2e0c8525ee8ca3..b1a278212ad3819ba3cda3abb152973c19eb092b 100644 --- a/translation.module +++ b/translation.module @@ -1,997 +1,1016 @@ -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('translate 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 node - if( arg(0) == 'node' && arg(1) == 'add' && isset($_GET['translation']) && isset($_GET['language']) && ($lang = $_GET['language']) && 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."), - ); - $form['i18n_translation_workflow'] = array( - '#type' => 'radios', - '#title' => t('Translation workflow'), - '#default_value' => variable_get('i18n_translation_workflow', 1), - '#options' => array(t('Disabled'), t('Enabled')), - '#description' => t("If enabled some worklow will be provided for content translation."), - ); - return $form; -} -/** - * Implementation of hook_blok(). - * - * 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) { - // 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(); - // Translation workflow, default - if ($node->nid && $node->translation) { - $translations = $node->translation; - } - elseif (isset($_GET['translation']) && user_access('translate nodes')) { - // We are translating a node: node/add/type/translation/nid/lang - $translation_nid = $_GET['translation']; - $language = $_GET['language']; - // 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); - } - // Translations are taken from source node - $translations = $trans->translation; - $translations[$trans->language] = $trans; - } - 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) - ); - } - // Translation workflow - if (variable_get('i18n_translation_workflow', 1) && (user_access('translate nodes') || user_access('administer nodes'))) { - $form['i18n']['i18n_status'] = array( - '#type' => 'select', - '#title' => t('Translation workflow'), - '#options' => _translation_status(), - '#description' => t('Use the translation workflow to keep track of content that needs translation.')); - if($node->nid) { - $form['i18n']['i18n_status']['#default_value'] = isset($node->i18n_status) ? $node->i18n_status : TRANSLATION_STATUS_NONE; - } elseif(isset($trans)) { - $form['i18n']['i18n_status']['#default_value'] = TRANSLATION_STATUS_WORKING; - } - } else { - $form['i18n']['i18n_status'] = array('#type' => 'value', '#value' => isset($node->i18n_status) ? $node->i18n_status : TRANSLATION_STATUS_NONE); - } - // Clone files for original node ? - if (isset($trans) && is_array($trans->files) && count($trans->files)) { - $form['i18n']['translation_files'] = array( - '#type' => 'fieldset', - '#title' => t('Files from translated content'), - '#tree' => TRUE, - '#prefix' => '
    ', - '#suffix' => '
    ', - '#theme' => 'upload_form_current', - '#description' => t('You can remove the files for this translation or keep the original files and translate the description.') - ); - foreach($trans->files as $key => $file) { - $description = file_create_url((strpos($file->fid, 'upload') === false ? $file->filepath : file_create_filename($file->filename, file_create_path()))); - $description = "". check_plain($description) .""; - $form['i18n']['translation_files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => (strlen($file->description)) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => $description ); - $form['i18n']['translation_files'][$key]['size'] = array('#type' => 'markup', '#value' => format_size($file->filesize)); - $form['i18n']['translation_files'][$key]['remove'] = array('#type' => 'checkbox', '#default_value' => 0); - $form['i18n']['translation_files'][$key]['list'] = array('#type' => 'checkbox', '#default_value' => $file->list); - $form['i18n']['translation_files'][$key]['fid'] = array('#type' => 'value', '#value' => $file->fid); - } - } - } -} - -/** - * Implementation of 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) { - // If not existing translation set, update both nodes. Otherwise trid is saved by i18n module - if(!$node->trid){ - $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); - } - // Clone files for node attachments - if(isset($node->translation_files)) { - foreach($node->translation_files as $fid => $file) { - if(!$file['remove']) { - // We are using revisions to have a file linked to different nodes, different descriptions - db_query("INSERT INTO {file_revisions} (fid, vid, list, description) VALUES (%d, %d, %d, '%s')", $file['fid'], $node->vid, $file['list'], $file['description']); - } - } - } - } - break; - } - } -} - -/** - * Fills up some fields from source node - */ -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); - } - } - // 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); - // If node has no language, just warning message. Function returns here - if(!$node->language) { - form_set_error('language', t("You need to set a language before creating a translation.")); - drupal_goto("node/$nid/edit"); - } - 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); - } - return $output; -} - -/** - * Menu callback: administration page for node translations - */ -function translation_node_admin() { - $output = ''; - $defaults = array('translation_language' => i18n_get_lang(), 'source_status' => TRANSLATION_STATUS_SOURCE); - $output .= translation_node_admin_form($defaults); - return $output; -} - -/** - * Administrative form for node translations - */ -function translation_node_admin_form($defaults = array()) { - $output = ''; - // Filters - $output .= node_filter_form(); - $output .= translation_filter_form($defaults); - - // First, translation filter because it may need parameters for join conditions - $filter = translation_build_filter_query(); - $where = $filter['where']; - $params = $filter['args']; - $join = $filter['join']; - - $filter = node_build_filter_query(); - // Remove WHERE - if($filter['where']) { - $where += array(str_replace('WHERE', '', $filter['where'])); - } - - $params = array_merge($params, $filter['args']); - - $join .= $filter['join']; - - // Select only source nodes - /* - $params[] = TRANSLATION_STATUS_SOURCE; - $where[] = "i.status = %d"; - */ - $sql = "SELECT n.nid, n.type, n.title, n.status, u.name, u.uid, i.language, i2.nid AS translation_nid, i2.status AS translation_status ". - "FROM {node} n INNER JOIN {users} u ON n.uid = u.uid INNER JOIN {i18n_node} i ON n.nid = i.nid ". - "LEFT JOIN {i18n_node} i2 ON i.trid = i2.trid AND 0 != i2.trid AND i.language != i2.language $join". - (count($where) ? ' WHERE '.implode(' AND ', $where) : ''); - - // Fetch data - //drupal_set_message("DEBUG:query: $sql"); - //drupal_set_message("DEBUG:params: ".implode(', ', $params)); - i18n_selection_mode('off'); - $result = pager_query(db_rewrite_sql($sql), 50, 0, NULL, $params); - i18n_selection_mode('reset'); - - $languages = i18n_supported_languages(); - $translation_status = _translation_status(); - - // Fetch language for translations - $language = isset($_SESSION['translation_filter']['translation_language']) ? $_SESSION['translation_filter']['translation_language'] : i18n_get_lang(); - $destination = drupal_get_destination(); - while ($node = db_fetch_object($result)) { - $nodes[$node->nid] = ''; - $form['language'][$node->nid] = array('#value' => $languages[$node->language]); - $form['title'][$node->nid] = array('#value' => l($node->title, 'node/'. $node->nid) .' '. theme('mark', node_mark($node->nid, $node->changed))); - $form['name'][$node->nid] = array('#value' => node_get_name($node)); - $form['username'][$node->nid] = array('#value' => theme('username', $node)); - $form['status'][$node->nid] = array('#value' => ($node->status ? t('published') : t('not published'))); - if ($node->translation_nid) { - $form['translation_status'][$node->nid] = array('#value' => $translation_status[$node->translation_status]); - $form['operations'][$node->nid] = array('#value' => l(t('edit translation'), 'node/'. $node->translation_nid .'/edit', array(), $destination)); - } else { - $form['translation_status'][$node->nid] = array('#value' => '--'); - if($language == $node->language) { - $form['operations'][$node->nid] = array('#value' => '--'); - } else { - $form['operations'][$node->nid] = array('#value' => l(t('create translation'), 'node/add/'.$node->type, array(), "translation=$node->nid&language=$language"). - ' | '.l(t('select node'), "node/$node->nid/translation/select/$language", array(), $destination)); - } - } - } - /* - $form['nodes'] = array('#type' => 'checkboxes', '#options' => $nodes); - */ - $form['pager'] = array('#value' => theme('pager', NULL, 50, 0)); - $output .= drupal_get_form('translation_admin_nodes', $form); - return $output; -} - - -/** - * Build query for node administration filters based on session. - */ -function translation_build_filter_query() { - - // Build query - $where = $args = array(); - $join = ''; - // This will produce an empty join - if (!is_array($_SESSION['translation_filter'])) { - $_SESSION['translation_filter'] = array(); - } - foreach ($_SESSION['translation_filter'] as $type => $value) { - switch($type) { - case 'source_language': - $where[] = "i.language = '%s'"; - $args[] = $value; - break; - case 'translation_language': - $join .= " AND i2.language ='".db_escape_string($value)."' "; - break; - case 'source_status': - $where[] = "i.status = %d"; - $args[] = $value; - break; - case 'translation_status': - $join .= " AND i2.status = ".db_escape_string($value); - break; - } - } - - return array('where' => $where, 'join' => $join, 'args' => $args); -} - -/** - * Returns form for translation administration filters. - */ -function translation_filter_form($defaults = array()) { - $session = &$_SESSION['translation_filter']; - $session = is_array($session) ? $session : $defaults; - - // Save defaults for form reset - $form['_defaults'] = array('#type' => 'value', '#value' => $defaults); - $form['filters'] = array('#type' => 'fieldset', - '#title' => t('And translation conditions are'), - ); - $languages = i18n_supported_languages(); - // Translation and language conditions - - $form['filters']['source_language'] = array('#type' => 'select', '#title' => t('source language'), - '#options' => array('' => '') + $languages, '#default_value' => $session['source_language']); - $form['filters']['source_status'] = array('#type' => 'select', '#title' => t('source status'), - '#options' => array('' => '') + _translation_status(), '#default_value' => $session['source_status']); - $form['filters']['translation_language'] = array('#type' => 'select', '#title' => t('translation language'), - '#options' => array('' => '') + $languages, '#default_value' => $session['translation_language']); - $form['filters']['translation_status'] = array('#type' => 'select', '#title' => t('translation status'), - '#options' => array('' => '') + _translation_status(), '#default_value' => $session['translation_status']); - $form['filters']['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Filter')); - if (count($session)) { - $form['filters']['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset')); - } - return drupal_get_form('translation_filter_form', $form); -} - -function theme_translation_filter_form($form) { - $form['filters']['source_language']['#prefix'] = '
    '; - $form['filters']['source_status']['#suffix'] = ''; - $form['filters']['buttons']['#prefix'] = ''; - $form['filters']['buttons']['#suffix'] = '
    '; - return form_render($form); -} -/** - * Builds translation admin filters - */ -function translation_filter_form_submit($form_id, $form_values) { - $op = $_POST['op']; - $session = &$_SESSION['translation_filter']; - - switch ($op) { - case t('Filter'): - $session = array(); - foreach($form_values as $name => $value) { - if($value) $session[$name] = $value; - } - break; - case t('Reset'): - $session = $form_values['_defaults']; - break; - } - -} -/** - * Theme node administration overview. - */ -function theme_translation_admin_nodes($form) { - // Overview table: - $header = array(t('Language'), t('Title'), t('Type'), t('Author'), t('Status'), t('Translation status'), t('Operations')); - - $output .= form_render($form['options']); - if (isset($form['title']) && is_array($form['title'])) { - foreach (element_children($form['title']) as $key) { - $row = array(); - $row[] = form_render($form['language'][$key]); - $row[] = form_render($form['title'][$key]); - $row[] = form_render($form['name'][$key]); - $row[] = form_render($form['username'][$key]); - $row[] = form_render($form['status'][$key]); - $row[] = form_render($form['translation_status'][$key]); - $row[] = form_render($form['operations'][$key]); - $rows[] = $row; - } - - } - else { - $rows[] = array(array('data' => t('No posts available.'), 'colspan' => '6')); - } - - $output .= theme('table', $header, $rows); - if ($form['pager']['#value']) { - $output .= form_render($form['pager']); - } - - $output .= form_render($form); - - return $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('Status'), 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); - $status = $trnode->status ? t('Published') : t('Not published'); - } else { - $title = t('Not translated'); - $options[] = l(t('create translation'), "node/add/$node->type", array(), "translation=$node->nid&language=$lang"); - $status = '--'; - } - $options[] = l(t('select node'), "node/$node->nid/translation/select/$lang"); - $rows[] = array($langname, $title, $status, 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; -} - -/** - * Form to select a translation from existing nodes - */ -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]) ); - } -} - -/** - * Process translation node form - */ -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')); - - } -} - -/** - * 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) { - // Add node link if published - if($trnode->status) { - $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; -} - -/** - * Returns node type for nid - */ -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; -} - -/** - * Produces a vocabulary translation form - */ -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); -} - -/** - * Process vocabulary translation 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; -} - -/** - * Get term translations - * - * @return - * An array of the from 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']; - } - $conds[] = "t.trid != 0"; - $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; -} - -/** - * Produces url of translated page - */ -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; -} - -/** - * Return status for translations workflow - */ -function _translation_status(){ - return array( - TRANSLATION_STATUS_NONE => t('None'), - TRANSLATION_STATUS_SOURCE => t('Source content (to be translated)'), - TRANSLATION_STATUS_WORKING => t('Translation in progress'), - TRANSLATION_STATUS_TRANSLATED => t('Translated content'), - TRANSLATION_STATUS_UPDATED => t('Source updated (to update translation)')); -} - -/** - * Theme a link to node translation - */ -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); -} - -/** - * Theme a link for a translation - */ -function theme_translation_link($text, $target, $lang, $separator=' ') { - return theme('i18n_link', $text, $target, $lang, $separator); -} - -/** - * Theme list of node translations - */ -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); -} -?> \ No newline at end of file +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/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/content/translation', + 'title' => t('Translations'), + 'description' => t("Manage content translations."), + 'callback' => 'translation_node_admin', + 'position' => 'left', + 'access' => user_access('translate nodes')); + $items[] = array('path' => 'admin/content/translation/overview', 'title' => t('List'), + 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10); + } + 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) == 'content' && arg(2) == 'taxonomy' && is_numeric(arg(3)) ) { + $items[] = array( + 'path' => 'admin/content/taxonomy/'.arg(3).'/translation', + 'title' => t('Translation'), + 'callback' => 'translation_taxonomy_admin', + 'access' => user_access('administer taxonomy'), + 'type' => MENU_LOCAL_TASK); + + } + // Change rewrite conditions when translating node + if( arg(0) == 'node' && arg(1) == 'add' && isset($_GET['translation']) && isset($_GET['language']) && ($lang = $_GET['language']) && 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(){ + return drupal_get_form('translation_settings_form'); +} + +/** + * Form builder function + */ +function translation_settings_form() { + $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."), + ); + $form['i18n_translation_workflow'] = array( + '#type' => 'radios', + '#title' => t('Translation workflow'), + '#default_value' => variable_get('i18n_translation_workflow', 1), + '#options' => array(t('Disabled'), t('Enabled')), + '#description' => t("If enabled some worklow will be provided for content translation."), + ); + return $form; +} +/** + * Implementation of hook_blok(). + * + * 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) { + // 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(); + // Translation workflow, default + if ($node->nid && $node->translation) { + $translations = $node->translation; + } + elseif (isset($_GET['translation']) && user_access('translate nodes')) { + // We are translating a node: node/add/type/translation/nid/lang + $translation_nid = $_GET['translation']; + $language = $_GET['language']; + // 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); + } + // Translations are taken from source node + $translations = $trans->translation; + $translations[$trans->language] = $trans; + } + 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) + ); + } + // Translation workflow + if (variable_get('i18n_translation_workflow', 1) && (user_access('translate nodes') || user_access('administer nodes'))) { + $form['i18n']['i18n_status'] = array( + '#type' => 'select', + '#title' => t('Translation workflow'), + '#options' => _translation_status(), + '#description' => t('Use the translation workflow to keep track of content that needs translation.')); + if($node->nid) { + $form['i18n']['i18n_status']['#default_value'] = isset($node->i18n_status) ? $node->i18n_status : TRANSLATION_STATUS_NONE; + } elseif(isset($trans)) { + $form['i18n']['i18n_status']['#default_value'] = TRANSLATION_STATUS_WORKING; + } + } else { + $form['i18n']['i18n_status'] = array('#type' => 'value', '#value' => isset($node->i18n_status) ? $node->i18n_status : TRANSLATION_STATUS_NONE); + } + // Clone files for original node ? + if (isset($trans) && is_array($trans->files) && count($trans->files)) { + $form['i18n']['translation_files'] = array( + '#type' => 'fieldset', + '#title' => t('Files from translated content'), + '#tree' => TRUE, + '#prefix' => '
    ', + '#suffix' => '
    ', + '#theme' => 'upload_form_current', + '#description' => t('You can remove the files for this translation or keep the original files and translate the description.') + ); + foreach($trans->files as $key => $file) { + $description = file_create_url((strpos($file->fid, 'upload') === false ? $file->filepath : file_create_filename($file->filename, file_create_path()))); + $description = "". check_plain($description) .""; + $form['i18n']['translation_files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => (strlen($file->description)) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => $description ); + $form['i18n']['translation_files'][$key]['size'] = array('#type' => 'markup', '#value' => format_size($file->filesize)); + $form['i18n']['translation_files'][$key]['remove'] = array('#type' => 'checkbox', '#default_value' => 0); + $form['i18n']['translation_files'][$key]['list'] = array('#type' => 'checkbox', '#default_value' => $file->list); + $form['i18n']['translation_files'][$key]['fid'] = array('#type' => 'value', '#value' => $file->fid); + } + } + } +} + +/** + * Implementation of 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) { + // If not existing translation set, update both nodes. Otherwise trid is saved by i18n module + if(!$node->trid){ + $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); + } + // Clone files for node attachments + if(isset($node->translation_files)) { + foreach($node->translation_files as $fid => $file) { + if(!$file['remove']) { + // We are using revisions to have a file linked to different nodes, different descriptions + db_query("INSERT INTO {file_revisions} (fid, vid, list, description) VALUES (%d, %d, %d, '%s')", $file['fid'], $node->vid, $file['list'], $file['description']); + } + } + } + } + break; + } + } +} + +/** + * Fills up some fields from source node + */ +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); + } + } + // 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); + // If node has no language, just warning message. Function returns here + if(!$node->language) { + form_set_error('language', t("You need to set a language before creating a translation.")); + drupal_goto("node/$nid/edit"); + } + 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); + } + return $output; +} + +/** + * Form builder function + */ +function translation_node_form($node, $lang, $list){ + $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(); + + $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 $form; +} + +/** + * Menu callback: administration page for node translations + */ +function translation_node_admin() { + $output = ''; + $defaults = array('translation_language' => i18n_get_lang(), 'source_status' => TRANSLATION_STATUS_SOURCE); + $output .= drupal_get_form('translation_node_admin', $defaults); + return $output; +} + +/** + * Form builder. Administrative form for node translations + */ +function translation_node_admin_form($defaults = array()) { + // Filters + $form['node_filter'] = node_filter_form(); + $form['translation_filter'] = translation_filter_form($defaults); + + // First, translation filter because it may need parameters for join conditions + $filter = translation_build_filter_query(); + $where = $filter['where']; + $params = $filter['args']; + $join = $filter['join']; + + $filter = node_build_filter_query(); + // Remove WHERE + if($filter['where']) { + $where += array(str_replace('WHERE', '', $filter['where'])); + } + + $params = array_merge($params, $filter['args']); + + $join .= $filter['join']; + + // Select only source nodes + /* + $params[] = TRANSLATION_STATUS_SOURCE; + $where[] = "i.status = %d"; + */ + $sql = "SELECT n.nid, n.type, n.title, n.status, u.name, u.uid, i.language, i2.nid AS translation_nid, i2.status AS translation_status ". + "FROM {node} n INNER JOIN {users} u ON n.uid = u.uid INNER JOIN {i18n_node} i ON n.nid = i.nid ". + "LEFT JOIN {i18n_node} i2 ON i.trid = i2.trid AND 0 != i2.trid AND i.language != i2.language $join". + (count($where) ? ' WHERE '.implode(' AND ', $where) : ''); + + // Fetch data + //drupal_set_message("DEBUG:query: $sql"); + //drupal_set_message("DEBUG:params: ".implode(', ', $params)); + i18n_selection_mode('off'); + $result = pager_query(db_rewrite_sql($sql), 50, 0, NULL, $params); + i18n_selection_mode('reset'); + + $languages = i18n_supported_languages(); + $translation_status = _translation_status(); + + // Fetch language for translations + $language = isset($_SESSION['translation_filter']['translation_language']) ? $_SESSION['translation_filter']['translation_language'] : i18n_get_lang(); + $destination = drupal_get_destination(); + while ($node = db_fetch_object($result)) { + $nodes[$node->nid] = ''; + $form['language'][$node->nid] = array('#value' => $languages[$node->language]); + $form['title'][$node->nid] = array('#value' => l($node->title, 'node/'. $node->nid) .' '. theme('mark', node_mark($node->nid, $node->changed))); + $form['name'][$node->nid] = array('#value' => node_get_name($node)); + $form['username'][$node->nid] = array('#value' => theme('username', $node)); + $form['status'][$node->nid] = array('#value' => ($node->status ? t('published') : t('not published'))); + if ($node->translation_nid) { + $form['translation_status'][$node->nid] = array('#value' => $translation_status[$node->translation_status]); + $form['operations'][$node->nid] = array('#value' => l(t('edit translation'), 'node/'. $node->translation_nid .'/edit', array(), $destination)); + } else { + $form['translation_status'][$node->nid] = array('#value' => '--'); + if($language == $node->language) { + $form['operations'][$node->nid] = array('#value' => '--'); + } else { + $form['operations'][$node->nid] = array('#value' => l(t('create translation'), 'node/add/'.$node->type, array(), "translation=$node->nid&language=$language"). + ' | '.l(t('select node'), "node/$node->nid/translation/select/$language", array(), $destination)); + } + } + } + /* + $form['nodes'] = array('#type' => 'checkboxes', '#options' => $nodes); + */ + $form['pager'] = array('#value' => theme('pager', NULL, 50, 0)); + return $form; +} + + +/** + * Build query for node administration filters based on session. + */ +function translation_build_filter_query() { + + // Build query + $where = $args = array(); + $join = ''; + // This will produce an empty join + if (!is_array($_SESSION['translation_filter'])) { + $_SESSION['translation_filter'] = array(); + } + foreach ($_SESSION['translation_filter'] as $type => $value) { + switch($type) { + case 'source_language': + $where[] = "i.language = '%s'"; + $args[] = $value; + break; + case 'translation_language': + $join .= " AND i2.language ='".db_escape_string($value)."' "; + break; + case 'source_status': + $where[] = "i.status = %d"; + $args[] = $value; + break; + case 'translation_status': + $join .= " AND i2.status = ".db_escape_string($value); + break; + } + } + + return array('where' => $where, 'join' => $join, 'args' => $args); +} + +/** + * Returns form for translation administration filters. + */ +function translation_filter_form($defaults = array()) { + $session = &$_SESSION['translation_filter']; + $session = is_array($session) ? $session : $defaults; + + // Save defaults for form reset + $form['_defaults'] = array('#type' => 'value', '#value' => $defaults); + $form['filters'] = array('#type' => 'fieldset', + '#title' => t('And translation conditions are'), + ); + $languages = i18n_supported_languages(); + // Translation and language conditions + + $form['filters']['source_language'] = array('#type' => 'select', '#title' => t('source language'), + '#options' => array('' => '') + $languages, '#default_value' => $session['source_language']); + $form['filters']['source_status'] = array('#type' => 'select', '#title' => t('source status'), + '#options' => array('' => '') + _translation_status(), '#default_value' => $session['source_status']); + $form['filters']['translation_language'] = array('#type' => 'select', '#title' => t('translation language'), + '#options' => array('' => '') + $languages, '#default_value' => $session['translation_language']); + $form['filters']['translation_status'] = array('#type' => 'select', '#title' => t('translation status'), + '#options' => array('' => '') + _translation_status(), '#default_value' => $session['translation_status']); + $form['filters']['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Filter')); + if (count($session)) { + $form['filters']['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset')); + } + return $form; +} + +function theme_translation_filter_form($form) { + $form['filters']['source_language']['#prefix'] = '
    '; + $form['filters']['source_status']['#suffix'] = ''; + $form['filters']['buttons']['#prefix'] = ''; + $form['filters']['buttons']['#suffix'] = '
    '; + return form_render($form); +} +/** + * Builds translation admin filters + */ +function translation_filter_form_submit($form_id, $form_values) { + $op = $_POST['op']; + $session = &$_SESSION['translation_filter']; + + switch ($op) { + case t('Filter'): + $session = array(); + foreach($form_values as $name => $value) { + if($value) $session[$name] = $value; + } + break; + case t('Reset'): + $session = $form_values['_defaults']; + break; + } + +} +/** + * Theme node administration overview. + */ +function theme_translation_admin_nodes($form) { + // Overview table: + $header = array(t('Language'), t('Title'), t('Type'), t('Author'), t('Status'), t('Translation status'), t('Operations')); + + $output .= form_render($form['options']); + if (isset($form['title']) && is_array($form['title'])) { + foreach (element_children($form['title']) as $key) { + $row = array(); + $row[] = form_render($form['language'][$key]); + $row[] = form_render($form['title'][$key]); + $row[] = form_render($form['name'][$key]); + $row[] = form_render($form['username'][$key]); + $row[] = form_render($form['status'][$key]); + $row[] = form_render($form['translation_status'][$key]); + $row[] = form_render($form['operations'][$key]); + $rows[] = $row; + } + + } + else { + $rows[] = array(array('data' => t('No posts available.'), 'colspan' => '6')); + } + + $output .= theme('table', $header, $rows); + if ($form['pager']['#value']) { + $output .= form_render($form['pager']); + } + + $output .= form_render($form); + + return $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('Status'), 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); + $status = $trnode->status ? t('Published') : t('Not published'); + } else { + $title = t('Not translated'); + $options[] = l(t('create translation'), "node/add/$node->type", array(), "translation=$node->nid&language=$lang"); + $status = '--'; + } + $options[] = l(t('select node'), "node/$node->nid/translation/select/$lang"); + $rows[] = array($langname, $title, $status, implode(" | ", $options)); + } + $output .= theme('table', $header, $rows); + if($node->trid){ + $output .= drupal_get_form('translation_node_overview_form', $form); + } + return $output; +} +function translation_node_overview_form($node) { + $form['submit'] = array('#type' => 'submit', '#value' => t('Remove'), '#suffix' => t('Remove node from this translation set')); + return $form; +} + +/** + * Form to select a translation from existing nodes + */ +function translation_node_select($node, $lang) { + $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){ + return drupal_get_form('translation_node_form', $node, $lang, $list); + } else { + return t("

    No nodes available in %language

    ", array('%language' => $languages[$lang]) ); + } +} + +/** + * Process translation node form + */ +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')); + + } +} + +/** + * Taxonomy hook + * $edit parameter is an array, not an object !! + */ + // $op = insert, update, delete +function translation_taxonomy($op, $type = NULL, $edit = NULL) { + if (!is_array($op)) { + 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; + } + } else { + return $op; + } +} +/** + * 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) { + // Add node link if published + if($trnode->status) { + $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; +} + +/** + * Returns node type for nid + */ +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(3); + $op = $_POST['op'] ? $_POST['op'] : arg(5); + $edit = $_POST['edit']; + + switch ($op) { + case t('Save'): + case 'edit': + drupal_set_title(t('Edit term translations')); + $output = translation_taxonomy_form($vid, arg(6), $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/content/taxonomy/$vid/translation/edit/$trid"); + $rows[] = $thisrow; + } + $output .= theme('table', $header, $rows); + $output .= l(t('new translation'), "admin/content/taxonomy/$vid/translation/edit/new"); + return $output; +} + +/** + * Produces a vocabulary translation form + */ +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); +} + +/** + * Process vocabulary translation 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; +} + +/** + * Get term translations + * + * @return + * An array of the from 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']; + } + $conds[] = "t.trid != 0"; + $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; +} + +/** + * Produces url of translated page + */ +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; +} + +/** + * Return status for translations workflow + */ +function _translation_status(){ + return array( + TRANSLATION_STATUS_NONE => t('None'), + TRANSLATION_STATUS_SOURCE => t('Source content (to be translated)'), + TRANSLATION_STATUS_WORKING => t('Translation in progress'), + TRANSLATION_STATUS_TRANSLATED => t('Translated content'), + TRANSLATION_STATUS_UPDATED => t('Source updated (to update translation)')); +} + +/** + * Theme a link to node translation + */ +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); +} + +/** + * Theme a link for a translation + */ +function theme_translation_link($text, $target, $lang, $separator=' ') { + return theme('i18n_link', $text, $target, $lang, $separator); +} + +/** + * Theme list of node translations + */ +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); +}