'.t('This module provides support for multilingual content in Drupal sites:').'
'; $output .= ''. t('For more information please read the on-line help pages.', array('@i18n' =>'http://drupal.org/node/31631')) .'
'; return $output; case 'admin/settings/i18n': $output .= ''.t('To enable multilingual support for specific content types go to !configure_content_types.', array('!configure_content_types' => l(t('configure content types'), 'admin/content/types'), )).'
'; return $output; } } /** * 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('Multilingual system'), 'description' => t('Configure multilingual content and translation.'), 'callback' => 'drupal_get_form', 'callback arguments' => array('i18n_admin_settings'), 'access' => user_access('administer site configuration'), ); $items[] = array( 'path' => 'admin/settings/i18n/main', 'title' => t('Internationalization'), 'type' => MENU_DEFAULT_LOCAL_TASK ); $items[] = array('path' => 'admin/settings/i18n/language', 'title' => t('Manage languages'), 'description' => t('Configure languages.'), 'callback' => 'locale_admin_manage', 'type' => MENU_LOCAL_TASK); } else { if (arg(0) == 'node') { if(isset($_POST['language']) && $_POST['language']) { $language = $_POST['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' && arg(0) == 'content' && user_access('administer all languages')) { // No restrictions for administration pages i18n_selection_mode('off'); } } return $items; } /** * Implementation of hook_nodeapi(). */ 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){ // Assign a trid from the beginning db_query("INSERT INTO {i18n_node} (nid, trid, language, status) VALUES(%d, '%d', '%s', '%d')", $node->nid, $node->trid, $node->language, $node->i18n_status); } // Handle menu items. Fixes duplication issue and language for menu items which happens when editing nodes in languages other than current. if (isset($node->menu) && !$node->menu['delete'] && $node->menu['title']) { $item = $node->menu; $item['path'] = ($item['path']) ? $item['path'] : "node/$node->nid"; $item['type'] = $item['type'] | MENU_MODIFIED_BY_ADMIN; if ($item['mid']) { // Update menu item db_query("UPDATE {menu} SET pid = %d, path = '%s', title = '%s', description = '%s', weight = %d, type = %d, language = '%s' WHERE mid = %d", $item['pid'], $item['path'], $item['title'], $item['description'], $item['weight'], $item['type'], $node->language, $item['mid']); drupal_set_message(t('The menu item %title has been updated with node language.', array('%title' => $item['title']))); } elseif(SAVED_NEW == menu_save_item($item)) { // Creating new menu item with node language db_query("UPDATE {menu} SET language = '%s' WHERE mid = %d", $node->language, $item['mid']); drupal_set_message(t('The menu item %title has been added with node language.', array('%title' => $item['title']))); } menu_rebuild(); unset($node->menu); // Avoid further processing by menu module } // Pathauto integration. Dynamic replacement of variables to allow different patterns per language if (module_exists('path') && module_exists('pathauto')) { // Language for pathauto variables is either node language or default language $language = $node->language ? $node->language : i18n_default_language(); if ($language != i18n_get_lang()) { i18n_variable_init($language, 'pathauto_node'); } } break; case 'delete': db_query('DELETE FROM {i18n_node} WHERE nid=%d', $node->nid); break; case 'prepare': // Book pages, set the right language nodes and outlines if (arg(3) == 'parent' && is_numeric(arg(4)) && ($parent = node_load(arg(4))) && $parent->language) { $node->language = $parent->language; i18n_selection_mode('node', $parent->language); } break; } } } /** * 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} SET language='%s' WHERE vid=%d", $edit['language'], $edit['vid']); drupal_set_message(t('Reset language for all terms.')); } break; } } /** * Implementation of hook_user() * * Switch to user's language after login */ function i18n_user($op, &$edit, &$account, $category = NULL) { if($op == 'login' && $account->language) { $_SESSION['language'] = $account->language; i18n_get_lang($account->language); } } /** * Form builder function. * * 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 * * @return array * Drupal Form */ function i18n_admin_settings(){ // Check languages $languages = variable_get('i18n_languages', array()); if (!count($languages) || ! count($languages['active']) > 1 ) { drupal_set_message(t('No languages enabled. Visit the !locale_admin page to set up your languages.', array('!locale_admin' => l(t('Manage languages'), 'admin/settings/locale/'))), 'error'); } $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("User browser language for home page and links without language prefix."), ); // 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".'), ); // Language prefix $form['prefix'] = array( '#type' => 'fieldset', '#title' => t('Language prefix'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#description' => t('Note: If you wish comparability with navigation mode (without language prefix), when module i18n is disabled, disable settings.'), ); $form['prefix']['i18n_prefix_default'] = array( '#type' => 'radios', '#title' => t('Language prefix for default language'), '#default_value' => variable_get('i18n_prefix_default', 0), '#options' => array(t('Disabled'), t('Enabled' )), '#description' => t("Add a language prefix to Drupal paths when the current language is the default language. Some established sites or single language sites with default language is not English may wish to disable language prefix of Drupal paths when enabling i18n module."), ); // 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); } /** * Simple i18n API */ /** * Get localized language list, sort alphabetically * * @param $all * TRUE for all languages, not only enabled */ function i18n_language_list($all = FALSE) { static $languages; $type = $all ? 'name' : 'active'; if(!isset($languages[$type])) { $languages[$type] = array_map('t', i18n_languages($type)); asort($languages[$type]); return $languages[$type]; } return $languages[$type]; } /** * Get list of supported languages * @param $all * TRUE to get all defined languages */ function i18n_supported_languages($all = FALSE) { return i18n_languages($all ? 'name' :'active'); } /** * Returns default language. * * @return string * Default language code. */ function i18n_default_language(){ return i18n_languages('site_default'); } /** * Check whether language is RTL * * @param $language * Language to check, defaults to current language */ function i18n_language_rtl($language = NULL) { $language = $language ? $language : i18n_get_lang(); return i18n_language_property($language, 'rtl') ? TRUE : FALSE; } /** * Get language properties * * @param $code * Language code. * @param $property * It may be 'name', 'native', 'ltr'... */ function i18n_language_property($code, $property = 'native') { $languages = i18n_languages($property); return $languages[$code]; } /** * Get locale languages plus i18n language settings * * @param $key * Data to be returned, defaults to 'active' * 'active' => array of enabled languages with native name * 'enabled' => array of enabled languages with english name * 'name' => all languages defined in locale module with english name * 'site_default' => code of default site language */ function i18n_languages($key = 'active') { static $languages; if (!$languages) { if($languages = variable_get('i18n_languages', 0)) { foreach($languages['name'] as $code => $name) { if($languages['enabled'][$code]) { $languages['active'][$code] = $name; } } } else { // It is possible that languages are not initialized if (function_exists('locale_supported_languages')) { $languages = locale_supported_languages(); } else { // Worst case scenario: locale module not loaded, for cached pages, at least this won't break everything unset($languages); // There's some PHP bug: http://www.zend.com/zend/week/week98.php $languages['name'] = array('en' => 'English'); } $languages['site_default'] = key($languages['name']); $languages['active'] = $languages['name']; $languages['native'] = $languages['name']; } // Sort everything alphabetically asort($languages['name']); asort($languages['active']); asort($languages['native']); } return $key ? $languages[$key] : $languages; } /** * Get language from browser settings, but only if it is a valid language * * @return string * Language code. */ function i18n_get_browser_lang() { static $browser_lang; if (!isset($browser_lang)) { $languages = i18n_supported_languages(); $exploded_server = explode(";", $_SERVER["HTTP_ACCEPT_LANGUAGE"]); $accept = explode(',', array_shift($exploded_server)); $browser_lang = ''; foreach ($accept as $lang) { if (($lang = @trim($lang)) && (array_key_exists(($lang = strtolower($lang)), $languages) || array_key_exists(($lang = substr($lang, 0, 2)), $languages))) { $browser_lang = $lang; break; } } } return $browser_lang; } /** * Get language code from path. * * @param string $path * Drupal path. * @param bool [optional] $trim * TRUE to remove language code from $path. * * @return string * Language code. */ function i18n_get_lang_prefix(&$path, $trim = FALSE){ $exploded_path = explode('/', $path); $maybelang = @strtolower(array_shift($exploded_path)); $languages = i18n_languages(); if (array_key_exists($maybelang, $languages)) { $last_lang = ''; $exploded_path_exists = count($exploded_path); foreach ($exploded_path as $k => $alt_lang) { if (!array_key_exists($alt_lang, $languages)) { break; } $last_lang = $alt_lang; unset($exploded_path[$k]); } // Normal Case // /uk => /uk if (!$exploded_path) { // Redirect for frontpage with many lang prefix // /uk/en/fr/ru => /uk if ($exploded_path_exists) { _i18n_goto($maybelang); } $path = $trim ? '' : $maybelang; } // Redirect for page with many lang prefix // 1. Translation if is node or taxonomy // /uk/ru/en/bla_bla => /en/bla_bla => /uk/bla_bla // 2. Or change lang // /uk/ru/en/bla_bla => /uk/bla_bla elseif ($last_lang) { $new_path = join('/', $exploded_path); $last_lang_new_path = $last_lang .'/'. $new_path; if ($last_lang_new_path == ($new_path = drupal_get_normal_path($last_lang_new_path))) { $new_path = drupal_get_normal_path($new_path); } if (module_exists('translation') && function_exists('translation_url')) { $new_path = translation_url($new_path, $maybelang); } _i18n_goto($new_path); } // Normal Case: // 1. Without lang prefix: /en/bla_bla => /bla_bla elseif ($trim) { $path = substr($path, strlen($maybelang) + 1); } // 2. With lang prefix: /en/bla_bla => /en/bla_bla // else {} } else { $maybelang = ''; // NO Language code. } return $maybelang; } /** * Language dependent front page * This function will search for aliases like 'en/home', 'es/home'..., * but for default language return aliase without prefix * * @param string [optional] $lang * ISO 639 language code. * * @return string * Language dependent path of front page. */ function i18n_frontpage($lang = NULL) { static $paths = array(); $lang = $lang ? strtolower(@trim($lang)) : _i18n_get_lang(); if (!isset($paths[$lang])) { $path = _i18n_add_lang_prefix($lang) ? $lang.'/'.variable_get('site_frontpage','node') : variable_get('site_frontpage','node'); $paths[$lang] = i18n_get_normal_path($path); } return $paths[$lang]; } /** * This function is similar to drupal_get_normal_path, but language-aware * Also removes language from path * * @param string $path * Drupal path. * * @return string * Drupal 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 * * @param string $path * Drupal Path * @param string $lang * Language prefix * * @return string * Drupal path with language prefix. */ function i18n_path($path, $lang){ if (!$path || $path == i18n_frontpage($lang)) { return _i18n_add_lang_prefix($lang) ? $lang : ''; } if(($alias = drupal_lookup_path('alias', $path)) && (!($prefix = i18n_get_lang_prefix($alias, TRUE)) || ($prefix && $prefix == $lang))) { $path = $alias; } if (_i18n_add_lang_prefix($lang)) { $path = $lang .'/'. $path; } return $path; } /** * Get node language */ 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 ; } /** * Get allowed languages for node * * This allows node types to define its own language list implementing hook 'language_list' */ function i18n_node_language_list($node) { if ($languages = node_invoke($node, 'language_list')) { return $languages; // The node module manages its own language list } elseif(variable_get('i18n_node_'.$node->type, 0) == LANGUAGE_SUPPORT_EXTENDED) { return i18n_language_list(TRUE); // All defined languages } else { return i18n_language_list(); // All enabled languages } } /** * 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 * * @param $path * Drupal internal path * @param $query * Query string * @param $names * Names to use for the links. Defaults to native language names */ function i18n_get_links($path = '', $query = NULL, $names = NULL) { if($path == i18n_frontpage()) { $path = ''; } $names = $names ? $names : i18n_languages('native'); $links = array(); foreach (array_keys(i18n_supported_languages()) as $lang){ $links[$lang]= theme('i18n_link', $names[$lang], i18n_path($path, $lang), $lang, $query); } return $links; } /** * 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'; } drupal_goto($lang); } /** * 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"); } else { array_push($store, array($current_mode, $current_value)); $current_mode = $mode; $current_value = $params; } } // List of selection modes 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'), ); } // List of language support modes for content function _i18n_content_languages() { return array( LANGUAGE_SUPPORT_NONE => t('Disabled'), LANGUAGE_SUPPORT_NORMAL => t('Normal - All enabled languages will be allowed.'), LANGUAGE_SUPPORT_EXTENDED => t('Extended - All defined languages will be allowed.') ); } /** * @name Themeable functions * @{ */ /** * Produces a language link with the right flag */ function theme_i18n_link($text, $target, $lang, $query= NULL, $fragment = NULL){ $output = ''; $attributes = ($lang == i18n_get_lang()) ? array('class' => 'active') : NULL; $output .= l(theme('i18n_language_icon', $lang), $target, $attributes, $query, $fragment, FALSE, TRUE); $output .= " "; $output .= l($text, $target, $attributes, $query, $fragment, FALSE, TRUE); $output .= ''; return $output; } /** * Theme language icon * * This function can be overridden for no language icons */ 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 "