Skip to content
i18n.module 25.2 KiB
Newer Older
<?php
// $Id$
/**
 * Internationalization (i18n) module
 *
 * @author Jose A. Reyero, 2004
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
 * Module initialization
 * 
 * Get language from path if exists and Initialize i18n system
  */

/**
 * This one expects to be called first from common.inc
 * Warning: this will run also in the administer modules page when module disabled
function i18n_get_lang() {
  static $i18n_language;
  //see if the language is already set.
  if ($i18n_language) {
    return $i18n_language;
  } else {
    _i18n_init();
    return $i18n_language = _i18n_get_lang();
  }
}
/**
 * Minimum initialization
 */
function _i18n_init(){
  global $i18n_langpath;
  $path = _i18n_get_original_path();
  $i18n_langpath = i18n_get_lang_prefix($path);
}  
 * Implementation of hook_init()
 * 
 * Complete initialization. Only when module enabled.
 * May do a redirect from home page for not to get wrong versions in cache
 * Warning: when in bootstrap mode, this may be called before i18n_get_lang()
  global $i18n_langpath;
  $lang = i18n_get_lang();
  $path = _i18n_get_original_path();
  // Init selection mode
  i18n_selection_mode(variable_get('i18n_selection_mode', 'simple'));
  // Multi tables, for backwards compatibility and experimentation
  _i18n_set_db_prefix($lang);

  if ($path == '') { // Main page
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
    // Check for update or cron scripts to disable rewriting and redirection
    if(preg_match('|/(?!index\.php)\w+\.php|', request_uri())){
      i18n_selection_mode('off');
    } elseif( variable_get('cache',0) && $lang != i18n_default_language() ) {
      // Redirect to main page in $lang
      _i18n_goto($lang);
    } else {
      $_GET['q'] = i18n_frontpage($lang);
  } elseif ($lang == $path) { // When path is only language code
    $_GET['q'] = i18n_frontpage($lang); 
  elseif ($i18n_langpath) {
    //search alias with and without lang and remove lang.
    $_GET['q'] = i18n_get_normal_path($path);
  // If not in bootstrap, variable init
    //include drupal_get_path('module', 'i18n').'/i18n.inc';
    i18n_variable_init();    
/**
 * Implementation of hook_help().
 */
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
function i18n_help($section = 'admin/help#i18n' ) {
  switch ($section) {
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
    case 'admin/help#i18n' :
      return t('
        <p>This module provides support for multilingual content in Drupal sites:</p>
        <ul>
        <li>Translation of the user interface for anonymous users (combined with locale)</li>
        <li>Multi-language for content. Adds a language field for nodes and taxonomy vocabularies and terms</li>
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
        <li>Basic translation management</li>
        <li>Browser language detection</li>
        <li>Keeps the language setting accross consecutive requests using URL rewriting</li>
        <li>Provides a block for language selection and two theme functions: <i>i18n_flags</i> and <i>i18n_links</i></li>
        <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>' );
/**
 * 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'];
      } elseif( is_numeric(arg(1)) && $node = node_load(arg(1)) ) {
        // Node language when loading specific nodes
        $language = $node->language;
      }
      if($language) i18n_selection_mode('node', db_escape_string($language));
    } elseif(arg(0) == 'admin') {
      // No restrictions for administration pages
      i18n_selection_mode('off');
    }
  }

  return $items;
}
 * 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
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
function i18n_settings() {
  return drupal_get_form('i18n_settings_form');
}

/**
 * Form builder function
 */
function i18n_settings_form() {
  $form['i18n_browser'] = array(
    '#type' => 'radios',
    '#title' => t('Browser language detection'),
    '#default_value' => variable_get('i18n_browser', 0),
    '#options' => array(t('Disabled'), t('Enabled' )),
    '#description' => t('A description of this setting.'),
  );
  
  // Language icons
  $form['icons'] = array(
    '#type' => 'fieldset',
    '#title' => t('Language icons settings'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );
  $form['icons']['i18n_icon_path'] = array(
    '#type' => 'textfield',
    '#title' => t('Language icons path'),
    '#default_value' => variable_get('i18n_icon_path', drupal_get_path('module', 'i18n').'/flags/*.png'),
    '#size' => 70,
    '#maxlength' => 180,
    '#description' => t('Path for language icons, relative to Drupal installation. \'*\' is a placeholder for language code.'),
  );
  $form['icons']['i18n_icon_size'] = array(
    '#type' => 'textfield',
    '#title' => t('Language icons size'),
    '#default_value' => variable_get('i18n_icon_size', '16x12'),
    '#size' => 10,
    '#maxlength' => 10,
    '#description' => t('Image size for language icons, in the form "width x height".'),
  );
  // Advanced options
  $form['advanced'] = array(
    '#type' => 'fieldset',
    '#title' => t('Advanced settings'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['advanced']['i18n_selection_mode'] = array(
    '#type' => 'radios',
    '#title' => t('Content selection mode'),
    '#default_value' => variable_get('i18n_selection_mode', 'simple'),
    '#options' => _i18n_selection_mode(),
    '#description' => t('Determines which content to show depending on language.'),
  );
    
  return system_settings_form($form);
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
 * Get list of supported languages
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
function i18n_supported_languages() {
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
  static $languages;
  if ($languages) {
    return $languages;
  elseif($languages = variable_get('i18n_languages', 0)) {
    return $languages;
  }   
  elseif ($languages = _i18n_locale_supported_languages()) {
    return $languages;
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
    return array();
/**
 * Returns default language
 */
function i18n_default_language(){
  $languages = i18n_supported_languages(); 
  return key($languages);  
 * Get language from browser settings, but only if it is a valid language
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
function i18n_get_browser_lang() {
  $languages = i18n_supported_languages();
  $exploded_server = explode(";",$_SERVER["HTTP_ACCEPT_LANGUAGE"]);
  $accept=explode(',',array_shift($exploded_server));
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
  foreach ($accept as $lang) {
    if ( !empty($lang) && array_key_exists($lang, $languages)) {
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
      return $lang;
    }
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
 * Get language code from path.
 * @param $trim = TRUE to remove language code from $path
function i18n_get_lang_prefix(&$path, $trim = FALSE) {
  $exploded_path = explode('/', $path);
  $maybelang = array_shift($exploded_path);
  $languages = i18n_supported_languages();
  if(array_key_exists($maybelang, $languages)){
    if($trim) {
      $path = trim(substr($path, strlen($maybelang)),'/');
    }
    return $maybelang;
  }
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
 * Language dependent front page
 * This function will search for aliases like 'en/home', 'es/home'...
function i18n_frontpage($lang = NULL) {
  $lang = $lang ? $lang : _i18n_get_lang();
  return i18n_get_normal_path($lang.'/'.variable_get('site_frontpage','node'));
Jose Antonio Reyero del Prado's avatar
Jose Antonio Reyero del Prado committed
 * This function is similar to drupal_get_normal_path, but language-aware
 * Also removes language from path
 */
function i18n_get_normal_path($path) {
  $prefix = i18n_get_lang_prefix($path, TRUE);
  if(!$prefix || _i18n_is_bootstrap()){
    // If bootstrap, drupal_lookup_path is not defined
    return $path; 
  } // First, check alias with lang
  elseif($alias = drupal_lookup_path('source', $prefix.'/'.$path)){
    i18n_get_lang_prefix($alias, TRUE); // In case alias has language
    return $alias;
  } // Check alias without lang
  elseif($alias = drupal_lookup_path('source', $path)){
    i18n_get_lang_prefix($alias, TRUE);
    return $alias;
  else {
    return $path;
  }
/**
 * More i18n API
 */

/**
 * Produces i18n paths, with language prefix
 * If path is empty or site frontpage, path = 'lang'
 * Check for frontpage and search for alias before adding language
 */
function i18n_path($path, $lang) {
  if (!$path || $path == i18n_frontpage($lang)) {
    return $lang;
  } elseif($alias = drupal_lookup_path('alias', $path)) {
	  if($prefix = i18n_get_lang_prefix($alias)) {
      // This alias will be valid only if it has the same language
	    return ($prefix == $lang) ? $alias : $lang.'/'.$path;
	  } else { // Alias without language prefix
	    return $lang.'/'.$alias;
	  }
  } else { // Alias for language path will be searched later
    return $lang.'/'.$path;  
  } 
}

function i18n_node_get_lang($nid, $default = '') {
  $lang = db_result(db_query('SELECT language FROM {i18n_node} WHERE nid=%d',$nid));
  return $lang ? $lang : $default ;
}

/**
 * Returns main language, two letter code
 */
function i18n_get_main_lang($lang = NULL){
  $lang = $lang ? $lang : i18n_get_lang();
  return substr($lang, 2);
}

/**
 * Function i18n_get_links
 * 
 * Returns an array of links for all languages, with or without names/flags
 */
function i18n_get_links($path = '') {
  if($path == i18n_frontpage()) {
    $path = '';
  }
  foreach(i18n_supported_languages() as $lang => $name){
    $links[$lang]= theme('i18n_link', $name, i18n_path($path, $lang), $lang);
  }
  return $links;  
}

/**
 *	Gets language, checking in order:
 *
 *	1. Path language
 *	2. User language
 *	3. Browser language
 *	4. Default language
 */

function _i18n_get_lang() {
  global $user, $i18n_langpath;
  static $i18n_lang;
  // Check whether the language is already set.
  // Language not set, find one
  $languages = i18n_supported_languages();
  if ($i18n_langpath && array_key_exists($i18n_langpath,$languages)) {
    $i18n_lang = $i18n_langpath;
  }
  elseif ($user->uid && $user->language && array_key_exists($user->language,$languages)) {
    $i18n_lang = $user->language;
  }
  elseif (variable_get("i18n_browser",0) && $lang=i18n_get_browser_lang()) {
    $i18n_lang=$lang;
  }
  else {
    $i18n_lang=key($languages);
  }
  
  return $i18n_lang;
}

/**
 * Check whether we are in bootstrap mode
 */  
function _i18n_is_bootstrap(){
  return !function_exists('drupal_get_headers');
}    

/**
 * Sets db_prefix to given language
 */
function _i18n_set_db_prefix($lang) {
  global $db_prefix, $db_prefix_i18n;
  if (is_array($db_prefix_i18n)) {
    $db_prefix = array_merge($db_prefix, str_replace('**', $lang, $db_prefix_i18n));
  }
}

/**
 * To get the original path. 
 * Cannot use $_GET["q"] cause it may have been already changed
 */
function _i18n_get_original_path() {
  return isset($_REQUEST["q"]) ? trim($_REQUEST["q"],"/") : '';
}

/**
 * Returns list of enabled languages from locale module
 *
 * Some code borrowed from locale module.
 * And yes, if locale enabled, languages are cached twice. But better twice than never ;-)
 */
function _i18n_locale_supported_languages() {
  if(function_exists('locale_supported_languages')){
    $languages = locale_supported_languages();
    return $languages['name'];
  } else {
    $result = db_query('SELECT locale, name FROM {locales_meta} WHERE enabled = 1 ORDER BY isdefault DESC, name ASC');
    while ($row = db_fetch_object($result)) {
      $enabled[$row->locale] = $row->name;
    }
    return $enabled;
/**
 * Emulates drupal_goto, it may not be loaded yet
 */
function _i18n_goto($lang){
  if(!function_exists('drupal_goto')){
    require_once './includes/common.inc';
    require_once './includes/path.inc';   
/**
 * i18n_selection_mode
 * Allows several modes for query rewriting and to change them programatically
 * 	off = No language conditions inserted
 * 	simple = Only current language and no language
 * 	mixed = Only current and default languages
 *  strict = Only current language
 *  default = Only default language
 *  user = User defined, in the module's settings page
 *  params = Gets the stored params
 *  reset = Returns to previous
 *  custom = add custom where clause, like "%alias.language = 'en'"
function i18n_selection_mode($mode= NULL, $params= NULL){
  static $current_mode = 'simple';
  static $current_value = '';
  static $store = array();
  
  if(!$mode) {
    return $current_mode;
  } elseif($mode == 'params'){
    return $current_value;
  } elseif($mode == 'reset'){
    list($current_mode, $current_value) = array_pop($store);
    //drupal_set_message("i18n mode reset mode=$current_mode value=$current_value");
    array_push($store, array($current_mode, $current_value));
    $current_mode = $mode;
    $current_value = $params;
  } 
}

function _i18n_selection_mode(){
  return array(
    'simple' => t('Only current language and no language'),
    'mixed' => t('Only current and default languages and no language'),
    'default' => t('Only default language and no language'),    
    'strict' => t('Only current language'),
    'off' => t('All content. No language conditions apply'),    

/**
 * @name Themeable functions
 * @{
 */
  
/**
 * Produces a language link with the right flag
 */
function theme_i18n_link($text, $target, $lang, $separator='&nbsp;'){
  $output = '<span class="i18n-link">';
  $attributes = ($lang == i18n_get_lang()) ? array('class' => 'active') : NULL;
  $output .= l(theme('i18n_language_icon', $lang), $target, $attributes, NULL, NULL, FALSE, TRUE);
  $output .= $separator;
  $output .= l($text, $target, $attributes, NULL, NULL, FALSE, TRUE);
  $output .= '</span>';
  return $output;
}
 
function theme_i18n_language_icon($lang){
  if ($path = variable_get('i18n_icon_path', drupal_get_path('module', 'i18n').'/flags/*.png')) {
    $languages = i18n_supported_languages();
    $src = base_path().str_replace('*', $lang, $path);
    list($width, $height) = explode('x', variable_get('i18n_icon_size', '16x12'));
    $attribs = array('class' => 'i18n-icon', 'width' => $width, 'height' => $height, 'alt' => $languages[$lang]);
    return "<img src=\"$src\" ".drupal_attributes($attribs)." />";
  }  
}

/* @} */

/**
 * 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));
    }
  }
}