This repository has been archived on 2025-06-21. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
suitedesk/sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.module

1106 lines
41 KiB
Text

<?php
/**
* @file
* i18n taxonomy module
*
* Internationalization (i18n) package.
*
* This module groups together all existing i18n taxonomy functionality
* providing several options for taxonomy translation.
*
* Translates taxonomy term for selected vocabularies running them through the localization system.
* It also translates terms for views filters and views results.
*
* @author Jose A. Reyero, 2004
*/
/**
* Modes for multilingual vocabularies.
*/
// No multilingual options
define('I18N_TAXONOMY_NONE', 0);
// Localizable terms. Run through the localization system.
define('I18N_TAXONOMY_LOCALIZE', 1);
// Predefined language for a vocabulary and its terms.
define('I18N_TAXONOMY_LANGUAGE', 2);
// Per-language terms, translatable (referencing terms with different languages) but not localizable.
define('I18N_TAXONOMY_TRANSLATE', 3);
/**
* Implementation of hook_help().
*/
function i18ntaxonomy_help($path, $arg) {
switch ($path) {
case 'admin/help#i18ntaxonomy' :
$output = '<p>'. t('This module adds support for multilingual taxonomy. You can set up multilingual options for each vocabulary:') .'</p>';
$output .= '<ul>';
$output .= '<li>'. t('A language can be assigned globaly for a vocabulary.') .'</li>';
$output .= '<li>'. t('Different terms for each language with translation relationships.') .'</li>';
$output .= '<li>'. t('Terms can be common to all languages, but may be localized.') .'</li>';
$output .= '</ul>';
$output .= '<p>'. t('To search and translate strings, use the <a href="@translate-interface">translation interface</a> pages.', array('@translate-interface' => url('admin/build/translate'))) .'</p>';
$output .= '<p>'. t('For more information, see the online handbook entry for <a href="@i18n">Internationalization module</a>.', array('@i18n' => 'http://drupal.org/node/133977')) .'</p>';
return $output;
case 'admin/settings/language/i18n':
$output = '<ul>';
$output .= '<li>'. t('To set up multilingual options for vocabularies go to <a href="@configure_taxonomy">Taxonomy configuration page</a>.', array('@configure_taxonomy' => url('admin/content/taxonomy'))) .'</li>';
$output .= '</ul>';
return $output;
case 'admin/content/taxonomy/%':
$vocabulary = taxonomy_vocabulary_load($arg[3]);
switch (i18ntaxonomy_vocabulary($vocabulary->vid)) {
case I18N_TAXONOMY_LOCALIZE:
return '<p>'. t('%capital_name is a localizable vocabulary. You will be able to translate term names and descriptions using the <a href="@translate-interface">translate interface</a> pages.', array('%capital_name' => drupal_ucfirst($vocabulary->name), '%name' => $vocabulary->name, '@translate-interface' => url('admin/build/translate'))) .'</p>';
case I18N_TAXONOMY_LANGUAGE:
return '<p>'. t('%capital_name is a vocabulary with a fixed language. All the terms in this vocabulary will have %language language.', array('%capital_name' => drupal_ucfirst($vocabulary->name), '%name' => $vocabulary->name, '%language' => i18n_language_property($vocabulary->language, 'name'))) .'</p>';
case I18N_TAXONOMY_TRANSLATE:
return '<p>'. t('%capital_name is a full multilingual vocabulary. You will be able to set a language for each term and create translation relationships.', array('%capital_name' => drupal_ucfirst($vocabulary->name))) .'</p>';
}
}
}
/**
* Returns list of vocabulary modes.
*/
function _i18ntaxonomy_vocabulary_options() {
return array(
I18N_TAXONOMY_NONE => t('None. No multilingual options for this vocabulary.'),
I18N_TAXONOMY_LOCALIZE => t('Localize terms. Terms are common for all languages, but their name and description may be localized.'),
I18N_TAXONOMY_TRANSLATE => t('Per language terms. Different terms will be allowed for each language and they can be translated.'),
I18N_TAXONOMY_LANGUAGE => t('Set language to vocabulary. The vocabulary will have a global language and it will only show up for pages in that language.'),
);
}
/**
* Implementation of hook_menu().
*/
function i18ntaxonomy_menu() {
$items['admin/content/taxonomy/%taxonomy_vocabulary/translation'] = array(
'title' => 'Translation',
'page callback' => 'i18ntaxonomy_page_vocabulary',
'page arguments' => array(3, 5, 6),
'access callback' => '_i18ntaxonomy_translation_tab',
'access arguments' => array(3),
'type' => MENU_LOCAL_TASK,
'parent' => 'admin/content/taxonomy/%taxonomy_vocabulary',
'file' => 'i18ntaxonomy.admin.inc',
);
return $items;
}
/**
* Implementation of hook_menu_alter().
*
* Take over the taxonomy pages
*/
function i18ntaxonomy_menu_alter(&$items) {
// If ctool's page manager is active for the path skip this modules override.
if (variable_get('page_manager_term_view_disabled', TRUE)) {
// Taxonomy term page. Localize terms.
$items['taxonomy/term/%']['module'] = 'i18ntaxonomy';
$items['taxonomy/term/%']['page callback'] = 'i18ntaxonomy_term_page';
$items['taxonomy/term/%']['file'] = 'i18ntaxonomy.pages.inc';
}
// Localize autocomplete
$items['taxonomy/autocomplete']['module'] = 'i18ntaxonomy';
$items['taxonomy/autocomplete']['page callback'] = 'i18ntaxonomy_autocomplete';
$items['taxonomy/autocomplete']['file'] = 'i18ntaxonomy.pages.inc';
}
/**
* Menu access callback. Show tab only for full multilingual vocabularies.
*/
function _i18ntaxonomy_translation_tab($vocabulary) {
return i18ntaxonomy_vocabulary($vocabulary->vid) == I18N_TAXONOMY_TRANSLATE;
}
/**
* Implementation of hook_locale().
*/
function i18ntaxonomy_locale($op = 'groups', $group = NULL) {
switch ($op) {
case 'groups':
return array('taxonomy' => t('Taxonomy'));
case 'info':
$info['taxonomy']['refresh callback'] = 'i18ntaxonomy_locale_refresh';
$info['taxonomy']['format'] = FALSE;
return $info;
}
}
/**
* Refresh strings.
*/
function i18ntaxonomy_locale_refresh() {
foreach (taxonomy_get_vocabularies() as $vid => $vocabulary) {
if (empty($vocabulary->language)) {
i18nstrings_update("taxonomy:vocabulary:$vid:name", $vocabulary->name);
if ($vocabulary->help) {
i18nstrings_update("taxonomy:vocabulary:$vid:help", $vocabulary->help);
}
}
if (i18ntaxonomy_vocabulary($vid) == I18N_TAXONOMY_LOCALIZE) {
foreach (taxonomy_get_tree($vid, 0) as $term) {
i18nstrings_update("taxonomy:term:$term->tid:name", $term->name);
if ($term->description) {
i18nstrings_update("taxonomy:term:$term->tid:description", $term->description);
}
}
}
}
return TRUE; // Meaning it completed with no issues
}
/**
* Implementation of hook_alter_translation_link().
*
* Replaces links with pointers to translated versions of the content.
*/
function i18ntaxonomy_translation_link_alter(&$links, $path) {
if (preg_match("/^(taxonomy\/term\/)([^\/]*)(.*)$/", $path, $matches)) { //or at a taxonomy-listing?
foreach ($links as $langcode => $link) {
if ($str_tids = i18ntaxonomy_translation_tids($matches[2], $langcode)) {
$links[$langcode]['href'] = "taxonomy/term/$str_tids". $matches[3];
}
}
}
}
/**
* Implementation of hook_theme().
*/
function i18ntaxonomy_theme() {
return array(
'i18ntaxonomy_term_page' => array(
'arguments' => array('tids' => array(), 'result' => NULL),
'file' => 'i18ntaxonomy.pages.inc',
),
);
}
/**
* Translate term name
*
* @param $tid
* Term id or term object
* @param $name
* Filtered default(untranslated) name
*/
function i18ntaxonomy_translate_term_name($tid, $name = '', $langcode = NULL) {
// If it is a term object we check for vocabulary options
if (is_object($tid)) {
return i18ntaxonomy_vocabulary($tid->vid) == I18N_TAXONOMY_LOCALIZE ? i18nstrings_string("taxonomy:term:$tid->tid:name", $tid->name, $langcode, TRUE) : check_plain($tid->name);
}
else {
return i18nstrings_string("taxonomy:term:$tid:name", $name, $langcode);
}
}
/**
* Translate vocabulary name
*
* @param $vid
* Vocabulary id or vocabulary object
* @param $name
* Filtered default(untranslated) name
*/
function i18ntaxonomy_translate_vocabulary_name($vid, $name = '', $langcode = NULL) {
return is_object($vid) ? i18nstrings_string("taxonomy:vocabulary:$vid->vid:name", $vid->name, $langcode, TRUE) : i18nstrings_string("taxonomy:vocabulary:$vid:name", $name, $langcode);
}
/**
* Get translated term's tid.
*
* @param $tid
* Node nid to search for translation.
* @param $language
* Language to search for the translation, defaults to current language.
* @param $default
* Value that will be returned if no translation is found.
* @return
* Translated term tid if exists, or $default.
*/
function i18ntaxonomy_translation_term_tid($tid, $language = NULL, $default = NULL) {
$translation = db_result(db_query("SELECT t.tid FROM {term_data} t INNER JOIN {term_data} a ON t.trid = a.trid AND t.tid <> a.tid WHERE a.tid = %d AND t.language = '%s' AND t.trid > 0", $tid, $language ? $language : i18n_get_lang()));
return $translation ? $translation : $default;
}
/**
* Returns an url for the translated taxonomy-page, if exists.
*/
function i18ntaxonomy_translation_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);
}
elseif (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 = i18ntaxonomy_translation_term_tid($tid, $lang)) {
$translated_tids[] = $translated_tid;
}
}
return implode($separator, $translated_tids);
}
/**
* Implementation of hook_taxonomy().
*
* $edit parameter may be an array or an object !!
*/
function i18ntaxonomy_taxonomy($op, $type, $edit = NULL) {
global $language;
$edit = (array)$edit;
switch ("$type/$op") {
case 'term/insert':
case 'term/update':
switch (i18ntaxonomy_vocabulary($edit['vid'])) {
case I18N_TAXONOMY_LOCALIZE: // Update strings for localizable vocabulary.
$tid = $edit['tid'];
i18nstrings_update("taxonomy:term:$tid:name", $edit['name']);
i18nstrings_update("taxonomy:term:$tid:description", $edit['description']);
break;
case I18N_TAXONOMY_LANGUAGE; // Predefined language for all terms
if (empty($edit['language']) && ($voc = taxonomy_vocabulary_load($edit['vid']))) {
_i18ntaxonomy_term_set_lang($edit['tid'], $voc->language);
}
break;
case I18N_TAXONOMY_TRANSLATE: // Multilingual terms, translatable
if (empty($edit['language'])) {
if (!empty($edit['i18ntaxonomy_form'])) {
// Only for the case the term has no language, it may need to be removed from translation set
_i18ntaxonomy_term_set_lang($edit['tid'], NULL);
} elseif($lang = _i18n_get_context_lang()) {
// The term may come from a node tags field, just if this is not a taxonomy form
_i18ntaxonomy_term_set_lang($edit['tid'], $lang);
} else {
// Not from the taxonomy form nor node form, set current language
_i18ntaxonomy_term_set_lang($edit['tid'], $language->language);
}
}
break;
}
break;
case 'vocabulary/insert':
case 'vocabulary/update':
$vid = $edit['vid'];
// Update vocabulary settings.
if (isset($edit['i18nmode'])) {
i18ntaxonomy_vocabulary($vid, $edit['i18nmode']);
$edit_lang = isset($edit['language']) ? $edit['language'] : '';
db_query("UPDATE {vocabulary} SET language='%s' WHERE vid = %d", $edit_lang, $edit['vid']);
if ($edit_lang && $op == 'update') {
db_query("UPDATE {term_data} SET language='%s' WHERE vid = %d", $edit_lang, $edit['vid']);
drupal_set_message(t('Reset language for all terms.'));
}
// Always add vocabulary translation if !$language.
if (!$edit_lang) {
i18nstrings_update("taxonomy:vocabulary:$vid:name", $edit['name']);
}
}
break;
case 'term/delete':
$tid = $edit['tid'];
i18nstrings_remove_string("taxonomy:term:$tid:name");
i18nstrings_remove_string("taxonomy:term:$tid:description");
break;
case 'vocabulary/delete':
$vid = $edit['vid'];
i18nstrings_remove_string("taxonomy:vocabulary:$vid:name");
break;
}
}
/**
* Implementation of hook_db_rewrite_sql().
*/
function i18ntaxonomy_db_rewrite_sql($query, $primary_table, $primary_key) {
// No rewrite for administration pages or mode = off.
$mode = i18n_selection_mode();
if ($mode == 'off' || arg(0) == 'admin') return;
switch ($primary_table) {
case 't':
case 'v':
// Taxonomy queries.
// When loading specific terms, vocabs, language conditions shouldn't apply.
if (preg_match("/WHERE.* $primary_table\.tid\s*(=\s*\d|IN)/", $query)) return;
// Taxonomy for specific node, or when using the term_node table.
if (preg_match("/WHERE r\.nid = \%d/", $query)) return;
if (preg_match("/{term_node}/", $query)) return;
$result['where'] = i18n_db_rewrite_where($primary_table, 'taxonomy', $mode);
return $result;
}
}
/**
* Implementation of hook_form_alter().
*
* This is the place to add language fields to all forms.
*
* @ TO DO The vocabulary form needs some javascript.
*/
function i18ntaxonomy_form_alter(&$form, $form_state, $form_id) {
switch ($form_id) {
case 'taxonomy_overview_vocabularies':
$vocabularies = taxonomy_get_vocabularies();
$languages = locale_language_list('name');
foreach ($vocabularies as $vocabulary) {
if ($vocabulary->language) {
$form[$vocabulary->vid]['types']['#value'] .= '&nbsp;('. $languages[$vocabulary->language] .')';
}
}
break;
case 'taxonomy_overview_terms':
$mode = i18ntaxonomy_vocabulary($form['#vocabulary']['vid']);
if ($mode == I18N_TAXONOMY_TRANSLATE) {
$languages = locale_language_list('name');
foreach (element_children($form) as $key) {
if (isset($form[$key]['#term']) && ($lang = $form[$key]['#term']['language'])) {
$form[$key]['view']['#value'] .= '&nbsp;('. $languages[$lang] .')';
}
}
}
break;
case 'taxonomy_form_vocabulary': // Taxonomy vocabulary
if (!empty($form['vid']['#value'])) {
$vocabulary = taxonomy_vocabulary_load($form['vid']['#value']);
$mode = i18ntaxonomy_vocabulary($vocabulary->vid);
}
else {
$vocabulary = NULL;
$mode = I18N_TAXONOMY_NONE;
}
drupal_add_js(drupal_get_path('module', 'i18ntaxonomy') . '/i18ntaxonomy.js');
drupal_add_js(array('i18ntaxonomy_vocabulary_form' => array('I18N_TAXONOMY_LANGUAGE' => I18N_TAXONOMY_LANGUAGE)), 'setting');
$form['i18n'] = array(
'#type' => 'fieldset',
'#title' => t('Multilingual options'),
'#collapsible' => TRUE,
'#weight' => 0,
);
$form['i18n']['i18nmode'] = array(
'#type' => 'radios',
'#title' => t('Translation mode'),
'#options' => _i18ntaxonomy_vocabulary_options(),
'#default_value' => $mode,
'#description' => t('For localizable vocabularies, to have all terms available for translation visit the <a href="@locale-refresh">translation refresh</a> page.', array('@locale-refresh' => url('admin/build/translate/refresh'))),
);
$form['i18n']['language'] = array(
'#type' => 'select',
'#title' => t('Language'),
'#default_value' => $vocabulary && !empty($vocabulary->language) ? $vocabulary->language : '',
'#options' => array('' => '') + locale_language_list('name'),
'#description' => t('Language for this vocabulary. If set, it will apply to all terms in this vocabulary.'),
'#disabled' => ($vocabulary && $mode != I18N_TAXONOMY_LANGUAGE),
);
$form['#validate'][] = 'i18ntaxonomy_form_vocabulary_validate';
break;
case 'taxonomy_form_term': // Taxonomy term
// Check for confirmation forms
if (isset($form_state['confirm_delete']) || isset($form_state['confirm_parents'])) return;
$vocabulary = (object)$form['#vocabulary'];
$term = (object)$form['#term'];
// Mark form so we can know later when saving the term it came from a taxonomy form
$form['i18ntaxonomy_form'] = array('#type' => 'value', '#value' => 1);
// Add language field or not depending on taxonomy mode.
switch (i18ntaxonomy_vocabulary($vocabulary->vid)) {
case I18N_TAXONOMY_TRANSLATE:
$form['identification']['language'] = array(
'#type' => 'select',
'#title' => t('Language'),
'#default_value' => isset($term) && !empty($term->language) ? $term->language : '',
'#options' => array('' => '') + locale_language_list('name'),
'#description' => t('This term belongs to a multilingual vocabulary. You can set a language for it.'),
);
break;
case I18N_TAXONOMY_LANGUAGE:
$form['language'] = array(
'#type' => 'value',
'#value' => $vocabulary->language
);
$form['identification']['language_info'] = array('#value' => t('All terms in this vocabulary have a fixed language: %language', array('%language' => i18n_language_property($vocabulary->language, 'name'))));
break;
case I18N_TAXONOMY_LOCALIZE:
$form['language'] = array(
'#type' => 'value',
'#value' => ''
);
$form['identification']['name']['#description'] .= ' <strong>'. t('This name will be localizable. You can translate it using the <a href="@translate-interface">translate interface</a> pages.', array('@translate-interface' => url('admin/build/translate'))) .'</strong>';
$form['identification']['description']['#description'] .= ' <strong>'. t('This description will be localizable. You can translate it using the <a href="@translate-interface">translate interface</a> pages.', array('@translate-interface' => url('admin/build/translate'))) .'</strong>';
break;
case I18N_TAXONOMY_NONE:
default:
$form['language'] = array(
'#type' => 'value',
'#value' => ''
);
break;
}
break;
case 'search_form':
// Localize category selector in advanced search form.
if (!empty($form['advanced']) && !empty($form['advanced']['category'])) {
i18ntaxonomy_form_all_localize($form['advanced']['category']);
}
break;
default:
if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id
&& ($node = $form['#node']) && isset($form['taxonomy']) && !variable_get('taxonomy_override_selector', FALSE)) {
// Node form. Translate vocabularies.
i18ntaxonomy_node_form($form);
}
}
}
function i18ntaxonomy_form_vocabulary_validate($form, &$form_state) {
$language = !empty($form_state['values']['language']) ? $form_state['values']['language'] : '';
$mode = $form_state['values']['i18nmode'];
if ($mode != I18N_TAXONOMY_LANGUAGE && $language) {
form_set_error('language', t('Setting a vocabulary language only makes sense in the "Set language to vocabulary" translation mode. Either change to this mode or do not select a language.'));
}
elseif ($mode == I18N_TAXONOMY_LANGUAGE && !$language ) {
form_set_error('language', t('If selecting "Set language to vocabulary" you need to set a language to this vocabulary. Either change the translation mode or select a language.'));
}
}
/**
* Localize a taxonomy_form_all() kind of control
*
* The options array is indexed by vocabulary name and then by term id, with tree structure
* We just need to localize vocabulary name and localizable terms. Multilingual vocabularies
* should have been taken care of by query rewriting.
**/
function i18ntaxonomy_form_all_localize(&$item) {
$options = &$item['#options'];
foreach (taxonomy_get_vocabularies() as $vid => $vocabulary) {
if (!empty($options[$vocabulary->name])) {
// Localize vocabulary name if translated
$vname = i18ntaxonomy_translate_vocabulary_name($vocabulary->name);
if ($vname != $vocabulary->name) {
$options[$vname] = $options[$vocabulary->name];
unset($options[$vocabulary->name]);
}
if (i18ntaxonomy_vocabulary($vid) == I18N_TAXONOMY_LOCALIZE) {
$tree = taxonomy_get_tree($vid);
if ($tree && (count($tree) > 0)) {
foreach ($tree as $term) {
if (isset($options[$vname][$term->tid])) {
$options[$vname][$term->tid] = str_repeat('-', $term->depth) . i18ntaxonomy_translate_term_name($term->tid, $term->name);
}
}
}
}
}
}
}
/**
* Handle node form taxonomy.
*/
function i18ntaxonomy_node_form(&$form) {
$node = $form['#node'];
if (!isset($node->taxonomy)) {
$terms = taxonomy_node_get_terms($node);
}
else {
$terms = $node->taxonomy;
}
// Regenerate the whole field for translatable vocabularies.
foreach (element_children($form['taxonomy']) as $vid) {
if ($vid == 'tags') {
// Special treatment for tags, add some help texts
foreach (element_children($form['taxonomy']['tags']) as $vid) {
$type = i18ntaxonomy_vocabulary($vid);
if ($type == I18N_TAXONOMY_LOCALIZE || $type == I18N_TAXONOMY_TRANSLATE) {
$form['taxonomy']['tags'][$vid]['#title'] = i18ntaxonomy_translate_vocabulary_name($vid, $form['taxonomy']['tags'][$vid]['#title']);
$form['taxonomy']['tags'][$vid]['#description'] = i18nstrings("taxonomy:vocabulary:$vid:help", $form['taxonomy']['tags'][$vid]['#description']);
}
if ($type == I18N_TAXONOMY_LOCALIZE) {
$form['taxonomy']['tags'][$vid]['#description'] .= ' '. t('This is a localizable vocabulary, so only terms in %language are allowed here.', array('%language' => language_default('name')));
}
}
}
elseif (is_numeric($vid) && i18ntaxonomy_vocabulary($vid) == I18N_TAXONOMY_LOCALIZE) {
// Rebuild this vocabulary's form.
$vocabulary = taxonomy_vocabulary_load($vid);
// Extract terms belonging to the vocabulary in question.
$default_terms = array();
foreach ($terms as $term) {
if ($term->vid == $vid) {
$default_terms[$term->tid] = $term;
}
}
$form['taxonomy'][$vid] = i18ntaxonomy_vocabulary_form($vocabulary->vid, array_keys($default_terms));
$form['taxonomy'][$vid]['#weight'] = $vocabulary->weight;
$form['taxonomy'][$vid]['#required'] = $vocabulary->required;
$form['taxonomy'][$vid]['#description'] = i18nstrings("taxonomy:vocabulary:$vid:help", $vocabulary->help);
}
elseif (is_numeric($vid) && i18ntaxonomy_vocabulary($vid) == I18N_TAXONOMY_TRANSLATE) {
// Rebuild this vocabulary's form.
$vocabulary = taxonomy_vocabulary_load($vid);
$form['taxonomy'][$vid]['#title'] = i18ntaxonomy_translate_vocabulary_name($vid, $vocabulary->name);
$form['taxonomy'][$vid]['#description'] = i18nstrings("taxonomy:vocabulary:$vid:help", $vocabulary->help);
}
}
}
/**
* Generate a form element for selecting terms from a vocabulary.
* Translates all translatable strings.
*/
function i18ntaxonomy_vocabulary_form($vid, $value = 0, $help = NULL) {
$vocabulary = taxonomy_vocabulary_load($vid);
$help = ($help) ? $help : i18nstrings("taxonomy:vocabulary:$vid:help", $vocabulary->help);
if (!$vocabulary->multiple) {
$blank = ($vocabulary->required) ? t('- Please choose -') : t('- None selected -');
}
else {
$blank = ($vocabulary->required) ? 0 : t('- None -');
}
$tree = i18ntaxonomy_localize_terms(taxonomy_get_tree($vid));
return _i18ntaxonomy_term_select(i18ntaxonomy_translate_vocabulary_name($vocabulary), $value, $tree, $help, intval($vocabulary->multiple), $blank);
}
/**
* Produces tree for taxonomy vocabularies.
*
* The difference with _taxonomy_term_select() is that this function is passed the term tree
* that may be already localized or filtered by language
*/
function _i18ntaxonomy_term_select($title, $value, $tree, $description = '', $multiple = FALSE, $blank = '--', $exclude = array()) {
$options = array();
if ($blank) {
$options[''] = $blank;
}
if ($tree) {
foreach ($tree as $term) {
if (!in_array($term->tid, $exclude)) {
$choice = new stdClass();
$choice->option = array($term->tid => str_repeat('--', $term->depth) . $term->name);
$options[] = $choice;
}
}
}
return array(
'#type' => 'select',
'#title' => $title,
'#default_value' => $value,
'#options' => $options,
'#description' => $description,
'#multiple' => $multiple,
'#size' => $multiple ? min(9, count($options)) : 0,
'#weight' => -15,
'#theme' => 'taxonomy_term_select',
);
}
/**
* Helper function for
*/
/**
* Set language for a term. If no language set trid to 0 too.
*/
function _i18ntaxonomy_term_set_lang($tid, $langcode) {
if ($langcode) {
db_query("UPDATE {term_data} SET language='%s' WHERE tid = %d", $langcode, $tid);
} else {
db_query("UPDATE {term_data} SET language = '', trid = 0 WHERE tid = %d", $tid);
}
}
/**
* Multilingual Taxonomy.
*/
/**
* Get term translations for multilingual terms. This works for multilingual vocabularies.
*
* @param $params
* Array of query conditions. I.e. array('tid' => xxx)
* @param $getall
* Whether to get the original term too in the set or not.
*
* @return
* An array of the from lang => Term.
*/
function i18ntaxonomy_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;
}
/**
* Like nat_get_terms() but without caching.
*/
function i18ntaxonomy_nat_get_terms($nid) {
$return = array();
$result = db_query("SELECT td.* FROM {nat} n INNER JOIN {term_data} td USING (tid) WHERE n.nid = %d", $nid);
while ($term = db_fetch_object($result)) {
$return[$term->tid] = $term;
}
return $return;
}
/**
* Implementation of hook_nodeapi().
*
* Prepare node for translation.
*/
function i18ntaxonomy_nodeapi(&$node, $op, $teaser, $page) {
switch ($op) {
case 'view':
// This runs after taxonomy:nodeapi, so we just localize terms here.
if (!empty($node->taxonomy)) {
$node->taxonomy = i18ntaxonomy_localize_terms($node->taxonomy);
}
if ($node->type == 'forum' && ($vid = variable_get('forum_nav_vocabulary', '')) && i18ntaxonomy_vocabulary($vid)) {
if ($page && taxonomy_node_get_terms_by_vocabulary($node, $vid) && $tree = taxonomy_get_tree($vid)) {
// Breadcrumb navigation
$vocabulary = taxonomy_vocabulary_load($vid);
$breadcrumb[] = l(t('Home'), NULL);
$breadcrumb[] = l(i18nstrings("taxonomy:vocabulary:$vid:name", $vocabulary->name), 'forum');
// Translate node taxonomy terms. Sometimes there are no terms, like for search results...
if (!empty($node->taxonomy)) {
// Get the forum terms from the (cached) tree
foreach ($tree as $term) {
$forum_terms[] = $term->tid;
}
foreach ($node->taxonomy as $term_id => $term) {
if (in_array($term_id, $forum_terms)) {
$node->tid = $term_id;
}
}
if ($parents = taxonomy_get_parents_all($node->tid)) {
$parents = array_reverse($parents);
foreach ($parents as $p) {
$breadcrumb[] = l(i18nstrings("taxonomy:term:$term->tid:name", $p->name), 'forum/'. $p->tid);
}
}
}
drupal_set_breadcrumb($breadcrumb);
}
}
break;
case 'prepare translation':
$source = $node->translation_source;
// Taxonomy translation.
if (is_array($source->taxonomy)) {
// Set translated taxonomy terms.
$node->taxonomy = i18ntaxonomy_translate_terms($source->taxonomy, $node->language);
}
break;
}
}
/**
* Find all terms associated with the given node, ordered by vocabulary and term weight.
*
* Same as taxonomy_node_get_terms() but without static caching.
*/
function i18ntaxonomy_node_get_terms($node, $key = 'tid') {
$result = db_query(db_rewrite_sql('SELECT t.* FROM {term_node} r INNER JOIN {term_data} t ON r.tid = t.tid INNER JOIN {vocabulary} v ON t.vid = v.vid WHERE r.vid = %d ORDER BY v.weight, t.weight, t.name', 't', 'tid'), $node->vid);
$terms = array();
while ($term = db_fetch_object($result)) {
$terms[$term->$key] = $term;
}
return $terms;
}
/**
* Translate an array of taxonomy terms.
*
* Translates all terms with language, just passing over terms without it.
* Filter out terms with a different language
*
* @param $taxonomy
* Array of term objects or tids or multiple arrays or terms indexed by vid
* @param $langcode
* Language code of target language
* @param $fullterms
* Whether to return full $term objects, returns tids otherwise
* @return
* Array with translated terms: tid -> $term
* Array with vid and term array
*/
function i18ntaxonomy_translate_terms($taxonomy, $langcode, $fullterms = TRUE) {
$translation = array();
if (is_array($taxonomy) && $taxonomy) {
foreach ($taxonomy as $index => $tdata) {
if (is_array($tdata)) {
// Case 1: Index is vid, $tdata is an array of terms
$mode = i18ntaxonomy_vocabulary($index);
// We translate just some vocabularies: translatable, fixed language
// Fixed language ones may have terms translated, though the UI doesn't support it
if ($mode == I18N_TAXONOMY_LANGUAGE || $mode == I18N_TAXONOMY_TRANSLATE) {
$translation[$index] = i18ntaxonomy_translate_terms($tdata, $langcode, $filter, $fullterms);
}
elseif ($fullterms) {
$translation[$index] = array_map('_i18ntaxonomy_filter_terms', $tdata);
}
else {
$translation[$index] = array_map('_i18ntaxonomy_filter_tids', $tdata);
}
continue;
}
elseif (is_object($tdata)) {
// Case 2: This is a term object
$term = $tdata;
}
elseif (is_numeric($tdata) && ($tid = (int)$tdata)) {
// Case 3: This is a term tid, load the full term
$term = taxonomy_get_term($tid);
}
// Translate the term if we got it
if (empty($term)) {
// Couldn't identify term, pass through whatever it is
$translation[$index] = $tdata;
}
elseif ($term->language && $term->language != $langcode) {
$translated_terms = i18ntaxonomy_term_get_translations(array('tid' => $term->tid));
if ($translated_terms && !empty($translated_terms[$langcode])) {
$newterm = $translated_terms[$langcode];
$translation[$newterm->tid] = $fullterms ? $newterm : $newterm->tid;
}
}
else {
// Term has no language. Should be ok.
$translation[$index] = $fullterms ? $term : $term->tid;
}
}
}
return $translation;
}
/**
* Localize taxonomy terms for localizable vocabularies.
*
* @param $terms
* Array of term objects.
* @param $fields
* Object properties to localize.
* @return
* Array of terms with the right ones localized.
*/
function i18ntaxonomy_localize_terms($terms, $fields = array('name', 'description')) {
$localize = i18ntaxonomy_vocabulary(NULL, I18N_TAXONOMY_LOCALIZE);
foreach ($terms as $index => $term) {
if (in_array($term->vid, $localize)) {
// Clone objects just in case one of them is saved later
$terms[$index] = clone $term;
foreach ($fields as $property) {
$terms[$index]->$property = i18nstrings("taxonomy:term:$term->tid:$property", $term->$property);
}
}
}
return $terms;
}
/**
* Taxonomy vocabulary settings.
*
* - If $vid and not $value, returns mode for vid.
* - If $vid and $value, sets mode for vid.
* - If !$vid and !$value returns all settings.
* - If !$vid and $value returns all vids for this mode.
*
* @param $vid
* Vocabulary id.
* @param $value
* Vocabulary mode.
*
*/
function i18ntaxonomy_vocabulary($vid = NULL, $mode = NULL) {
$options = variable_get('i18ntaxonomy_vocabulary', array());
if ($vid && !is_null($mode)) {
$options[$vid] = $mode;
variable_set('i18ntaxonomy_vocabulary', $options);
}
elseif ($vid) {
return array_key_exists($vid, $options) ? $options[$vid] : I18N_TAXONOMY_NONE;
}
elseif (!is_null($mode)) {
return array_keys($options, $mode);
}
else {
return $options;
}
}
/**
* Returns a list for terms for vocabulary, language.
*
* @param $vid
* Vocabulary id
* @param $lang
* Language code
* @param $status
* 'all' (default), 'translated', 'untranslated'
*/
function i18ntaxonomy_vocabulary_get_terms($vid, $lang, $status = 'all') {
switch ($status) {
case 'translated':
$result = db_query("SELECT * FROM {term_data} WHERE vid = %d AND language = '%s' AND trid > 0", $vid, $lang);
break;
case 'untranslated':
$result = db_query("SELECT * FROM {term_data} WHERE vid = %d AND language = '%s' AND trid = 0", $vid, $lang);
break;
default:
$result = db_query("SELECT * FROM {term_data} WHERE vid = %d AND language = '%s'", $vid, $lang);
break;
}
$list = array();
while ($term = db_fetch_object($result)) {
$list[$term->tid] = $term->name;
}
return $list;
}
/**
* Get taxonomy tree for a given language
*
* @param $vid
* Vocabulary id
* @param $lang
* Language code
* @param $parent
* Parent term id for the tree
*/
function i18ntaxonomy_get_tree($vid, $lang, $parent = 0, $depth = -1, $max_depth = NULL) {
static $children, $parents, $terms;
$depth++;
// We cache trees, so it's not CPU-intensive to call get_tree() on a term
// and its children, too.
if (!isset($children[$vid][$lang])) {
$children[$vid][$lang] = array();
$result = db_query(db_rewrite_sql("SELECT t.tid, t.*, parent FROM {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE t.vid = %d AND t.language = '%s' ORDER BY weight, name", 't', 'tid'), $vid, $lang);
while ($term = db_fetch_object($result)) {
$children[$vid][$lang][$term->parent][] = $term->tid;
$parents[$vid][$lang][$term->tid][] = $term->parent;
$terms[$vid][$term->tid] = $term;
}
}
$max_depth = (is_null($max_depth)) ? count($children[$vid][$lang]) : $max_depth;
$tree = array();
if (!empty($children[$vid][$lang][$parent])) {
foreach ($children[$vid][$lang][$parent] as $child) {
if ($max_depth > $depth) {
$term = drupal_clone($terms[$vid][$child]);
$term->depth = $depth;
// The "parent" attribute is not useful, as it would show one parent only.
unset($term->parent);
$term->parents = $parents[$vid][$lang][$child];
$tree[] = $term;
if (!empty($children[$vid][$lang][$child])) {
$tree = array_merge($tree, i18ntaxonomy_get_tree($vid, $lang, $child, $depth, $max_depth));
}
}
}
}
return $tree;
}
/**
* Implementation of hook_token_values().
*/
function i18ntaxonomy_token_values($type, $object = NULL, $options = array()) {
$values = array();
switch ($type) {
case 'taxonomy':
$term = $object;
$values['i18n-term-raw'] = i18nstrings("taxonomy:term:$term->tid:name", $term->name);
$values['i18n-term'] = check_plain(i18nstrings("taxonomy:term:$term->tid:name", $term->name));
break;
case 'node':
$node = $object;
// This code is copied from the token module which i adapting
// pathauto's handling code; it's intended for compatability with it.
if (!empty($node->taxonomy) && is_array($node->taxonomy)) {
foreach ($node->taxonomy as $term) {
$original_term = $term;
if ((object)$term) {
// With freetagging it's somewhat hard to get the tid, vid, name values
// Rather than duplicating taxonomy.module code here you should make sure your calling module
// has a weight of at least 1 which will run after taxonomy has saved the data which allows us to
// pull it out of the db here.
if (!isset($term->name) || !isset($term->tid)) {
$vid = db_result(db_query_range("SELECT t.vid FROM {term_node} r INNER JOIN {term_data} t ON r.tid = t.tid INNER JOIN {vocabulary} v ON t.vid = v.vid WHERE r.nid = %d ORDER BY v.weight, t.weight, t.name", $object->nid, 0, 1));
if (!$vid) {
continue;
}
$term = db_fetch_object(db_query_range("SELECT t.tid, t.name FROM {term_data} t INNER JOIN {term_node} r ON r.tid = t.tid WHERE t.vid = %d AND r.nid = %d ORDER BY weight", $vid, $object->nid, 0, 1));
$term->vid = $vid;
}
// Ok, if we still don't have a term name maybe this is a pre-taxonomy submit node
// So if it's a number we can get data from it
if (!isset($term->name) && is_array($original_term)) {
$tid = array_shift($original_term);
if (is_numeric($tid)) {
$term = taxonomy_get_term($tid);
}
}
$vid = $term->vid;
// If term names are localizable, we translate them to the node's
// content language, not to the interface language' in which the
// current user is viewing the site. (Creation of node tokens should
// not depend on 'unpredictable' conditions like these.)
// If node is language neutral, language is set to NULL.
if (i18ntaxonomy_vocabulary($vid) == I18N_TAXONOMY_LOCALIZE && $node->language) {
$values['i18n-term-raw'] = i18nstrings("taxonomy:term:$term->tid:name", $term->name, $node->language);
$values['i18n-term'] = check_plain(i18nstrings("taxonomy:term:$term->tid:name", $term->name, $node->language));
}
else {
$values['i18n-term-raw'] = $term->name;
$values['i18n-term'] = check_plain($term->name);
}
break;
}
}
}
// It's possible to leave that block and still not have good data.
// So, we test for these and if not set, set them.
if (!isset($values['i18n-term'])) {
$values['i18n-term-raw'] = '';
$values['i18n-term'] = '';
}
break;
}
return $values;
}
/**
* Implementation of hook_token_list().
*/
function i18ntaxonomy_token_list($type = 'all') {
if ($type == 'node' || $type == 'all' || $type == 'taxonomy') {
$tokens['i18ntaxonomy']['i18n-term-raw'] = t("Unescaped term name translated using i18n");
$tokens['i18ntaxonomy']['i18n-term'] = t("Escaped term name translated using i18n");
return $tokens;
}
}
/**
* Translate forums list.
*/
function i18ntaxonomy_preprocess_forum_list(&$variables) {
$vid = variable_get('forum_nav_vocabulary', '');
if (i18ntaxonomy_vocabulary($vid)) {
foreach ($variables['forums'] as $id => $forum) {
$variables['forums'][$id]->description = i18nstrings('taxonomy:term:'. $forum->tid .':description', $forum->description);
$variables['forums'][$id]->name = i18nstrings('taxonomy:term:'. $forum->tid .':name', $forum->name);
}
}
}
/**
* Translate forum page.
*/
function i18ntaxonomy_preprocess_forums(&$variables) {
$vid = variable_get('forum_nav_vocabulary', '');
if (i18ntaxonomy_vocabulary($vid)) {
if (isset($variables['links']['forum'])) {
$variables['links']['forum']['title'] = i18nstrings('nodetype:type:forum:post_button', 'Post new Forum topic');
}
// This one is from advanced forum, http://drupal.org/project/advanced_forum
if ($variables['forum_description']) {
$variables['forum_description'] = i18nstrings('taxonomy:term:'. $variables['tid'] .':description', $variables['forum_description']);
}
$vocabulary = taxonomy_vocabulary_load($vid);
// Translate the page title.
$title = !empty($vocabulary->name) ? i18ntaxonomy_translate_vocabulary_name($vocabulary) : '';
drupal_set_title($title);
// Translate the breadcrumb.
$breadcrumb = array();
$breadcrumb[] = l(t('Home'), NULL);
$breadcrumb[] = l($title, 'forum');
drupal_set_breadcrumb($breadcrumb);
}
}
/**
* Recursive array filtering, convert all terms to 'tid'.
*/
function _i18ntaxonomy_filter_tids($tid) {
if (is_array($tid)) {
return array_map('_i18n_taxonomy_filter_tids', $tid);
}
else {
return is_object($tid) ? $tid->tid : (int)$tid;
}
}
/**
* Recursive array filtering, convert all terms to 'term object'
*/
function _i18ntaxonomy_filter_terms($term) {
if (is_array($term)) {
return array_map('_i18n_taxonomy_filter_terms', $term);
}
else {
return is_object($term) ? $term : taxonomy_get_term($term);
}
}