summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJose Reyero2006-11-29 15:22:06 (GMT)
committer Jose Reyero2006-11-29 15:22:06 (GMT)
commit74c2972ef86e860bbb6c206934c62743660a25e5 (patch)
tree9356933abc66e177eae7e3ef793a4b0792186b3d
parent22bc9319b7c2a490258bc58e0fef4fee4e39a4a5 (diff)
Starting Drupal 5 update
Partially committed #84093
-rw-r--r--INSTALL.txt2
-rw-r--r--contrib/i18nblocks.info5
-rw-r--r--contrib/i18nblocks.module19
-rw-r--r--contrib/i18nmenu.info5
-rw-r--r--contrib/i18nmenu.module7
-rw-r--r--i18n.inc20
-rw-r--r--i18n.info5
-rw-r--r--i18n.module333
-rw-r--r--i18nprofile/i18nprofile.info5
-rw-r--r--translation.info5
-rw-r--r--translation.module2013
11 files changed, 1389 insertions, 1030 deletions
diff --git a/INSTALL.txt b/INSTALL.txt
index bc3b0b3..c2dc67a 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 0000000..1fd3c36
--- /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 dc9ffe1..cacc0f0 100644
--- a/contrib/i18nblocks.module
+++ b/contrib/i18nblocks.module
@@ -17,10 +17,8 @@ function i18nblocks_help($section = 'admin/help#i18nblocks' ) {
<p>These are not real blocks, but metablocks that group together a number of normal blocks and display the right one depending on language</p>
<p>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.</p>
' );
- case 'admin/block/i18n':
+ case 'admin/build/block/i18n':
return t('<p>These are not real blocks, but metablocks that group together a number of normal blocks and display the right one depending on language</p>');
- case 'admin/modules#description' :
- return t('Manages multilingual meta-blocks. <b>Requires i18n and locale module</b>' );
}
}
@@ -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 0000000..65fd467
--- /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 ed155a9..f6d03cb 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' ) {
<li>Switch language to some non english one -while viewing the block-, so the <i>locales</i> table is populated with the new strings</li>
<li>Use the localization system to translate menu item strings</li>
</ul>' );
- case 'admin/modules#description' :
- return t('Supports translatable custom menu items. <b>Requires i18n and locale module</b>' );
}
return $output;
}
-?> \ No newline at end of file
diff --git a/i18n.inc b/i18n.inc
index 32acfdc..947d83c 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 0000000..f402c4a
--- /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 67f3b16..16cb0f3 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,18 +96,30 @@ function i18n_help($section = 'admin/help#i18n' ) {
<li>Support for long locale names</li>
</ul>
<p><small>Module developed by Jose A. Reyero, <a href="http://www.reyero.net">www.reyero.net</a></small></p>' );
- case 'admin/modules#description' :
- return t('Enables multilingual content. <b>Requires locale module for interface translation</b>' );
}
}
/**
+ * Implementation of hook_perm
+ */
+function i18n_perm(){
+ return array('administer i18n');
+}
+
+/**
* Implementation of hook_menu().
* Modify rewriting conditions when viewing specific nodes
*/
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'].'&nbsp('.$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 <i>Preview</i> to get the right Categories &amp; 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 0000000..12245d0
--- /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 0000000..8fbda1d
--- /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 9c6c3b4..b1a2782 100644
--- a/translation.module
+++ b/translation.module
@@ -1,997 +1,1016 @@
-<?php
-// $Id$
-
-/**
- * Internationalization (i18n) package.
- *
- * Translation module: translation
- *
- * @author Jose A. Reyero, 2004, http://www.reyero.net
- *
- */
-
-// Status for translations
-define('TRANSLATION_STATUS_NONE', 0);
-define('TRANSLATION_STATUS_SOURCE', 1);
-define('TRANSLATION_STATUS_WORKING', 2);
-define('TRANSLATION_STATUS_TRANSLATED', 3);
-define('TRANSLATION_STATUS_UPDATED', 4);
-
-/**
- * Implementation of hook_help().
- */
-function translation_help($section = 'admin/help#translation' ) {
- switch ($section) {
- case 'admin/help#translation' :
- $output = t('
- <p>This module is part of i18n package and provides support for translation relationships.</p>
- <p>The objects you can define translation relationships for are:</p>
- <ul>
- <li>Nodes</li>
- <li>Taxonomy Terms</li>
- </ul>
- <p><small>Module developed by Jose A. Reyero, <a href="http://www.reyero.net">www.reyero.net</a></small></p>' );
- break;
- case 'admin/modules#description' :
- $output = t('Manages translations between nodes and taxonomy terms. <b>Requires i18n module</b>' );
- break;
- case 'admin/access#translation':
- $output = t('<h2>Translations</h2>');
- $output = t('<strong>translate nodes</strong> <p>This one, combined with create content permissions, will allow to create node translation</p>');
- }
- return $output;
-}
-
-/**
- * Implementation of hook_menu().
- */
-function translation_menu($may_cache) {
- $items = array();
-
- if ($may_cache) {
- $items[] = array(
- 'path' => 'admin/node/translation',
- 'title' => t('translation'),
- 'callback' => 'translation_node_admin',
- 'access' => user_access('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' => '<div class="attachments">',
- '#suffix' => '</div>',
- '#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 = "<small>". check_plain($description) ."</small>";
- $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'] = '<table><tr><td>';
- $form['filters']['source_status']['#suffix'] = '</td><td>';
- $form['filters']['buttons']['#prefix'] = '</td><td>';
- $form['filters']['buttons']['#suffix'] = '</td></tr></table>';
- 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('<h2>Current translations</h2>');
- $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("<p>No nodes available in %language</p>", 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='&nbsp;') {
- 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
+<?php
+// $Id$
+
+/**
+ * Internationalization (i18n) package.
+ *
+ * Translation module: translation
+ *
+ * @author Jose A. Reyero, 2004, http://www.reyero.net
+ *
+ */
+
+// Status for translations
+define('TRANSLATION_STATUS_NONE', 0);
+define('TRANSLATION_STATUS_SOURCE', 1);
+define('TRANSLATION_STATUS_WORKING', 2);
+define('TRANSLATION_STATUS_TRANSLATED', 3);
+define('TRANSLATION_STATUS_UPDATED', 4);
+
+/**
+ * Implementation of hook_help().
+ */
+function translation_help($section = 'admin/help#translation' ) {
+ switch ($section) {
+ case 'admin/help#translation' :
+ $output = t('
+ <p>This module is part of i18n package and provides support for translation relationships.</p>
+ <p>The objects you can define translation relationships for are:</p>
+ <ul>
+ <li>Nodes</li>
+ <li>Taxonomy Terms</li>
+ </ul>
+ <p><small>Module developed by Jose A. Reyero, <a href="http://www.reyero.net">www.reyero.net</a></small></p>' );
+ break;
+ case 'admin/access#translation':
+ $output = t('<h2>Translations</h2>');
+ $output = t('<strong>translate nodes</strong> <p>This one, combined with create content permissions, will allow to create node translation</p>');
+ }
+ return $output;
+}
+
+/**
+ * Implementation of hook_menu().
+ */
+function translation_menu($may_cache) {
+ $items = array();
+
+ if ($may_cache) {
+ $items[] = array(
+ 'path' => 'admin/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' => '<div class="attachments">',
+ '#suffix' => '</div>',
+ '#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 = "<small>". check_plain($description) ."</small>";
+ $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'] = '<table><tr><td>';
+ $form['filters']['source_status']['#suffix'] = '</td><td>';
+ $form['filters']['buttons']['#prefix'] = '</td><td>';
+ $form['filters']['buttons']['#suffix'] = '</td></tr></table>';
+ 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('<h2>Current translations</h2>');
+ $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("<p>No nodes available in %language</p>", 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='&nbsp;') {
+ 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);
+}