'.t('This module improves support for multilingual content in Drupal sites:').'
';
$output .= '';
$output .= '- '.t('Shows content depending on page language').'
';
$output .= '- '.t('Handles multilingual variables').'
';
$output .= '- '.t('Extended language option for chosen content types. For these content types transations will be allowed for all defined languages, not only for enabled ones.').'
';
$output .= '- '.t('Provides a block for language selection and two theme functions: i18n_flags and i18n_links').'
';
$output .= '
';
$output .= ''.t('This is the base module for several others adding different features:').'
';
$output .= '';
$output .= '- '.t('Multilingual menu items').'
';
$output .= '- '.t('Multilingual taxonomy Adds a language field for taxonomy vocabularies and terms').'
';
$output .= '
';
$output .= ''. t('For more information, see the online handbook entry for Internationalization module.', array('@i18n' =>'http://drupal.org/node/133977')) .'
';
return $output;
case 'admin/settings/i18n':
$output = '';
$output .= '- '.t('To manage languages go to the languages configuration page.', array('@configure_languages' => url('admin/settings/language'))).'
';
$output .= '- '.t('To enable multilingual support for specific content types go to configure content types.', array('@configure_content_types' => url('admin/content/types'))).'
';
$output . '
';
return $output;
}
}
/**
* Implementation of hook_menu().
*/
function i18n_menu() {
$items['admin/settings/i18n'] = array(
'title' => 'Multilingual system',
'description' => 'Configure extended options for multilingual content and translations.',
'page callback' => 'drupal_get_form',
'page arguments' => array('i18n_admin_settings'),
'access arguments' => array('administer site configuration'),
'file' => 'i18n.admin.inc',
);
$items['admin/settings/i18n/main'] = array(
'title' => 'Internationalization',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
return $items;
}
/**
* Implementation of hook_menu_alter().
*
* Take over the node translation page
*/
function i18n_menu_alter(&$items) {
// dsm($router_items);
/*
$items['node/%node/translate']['page callback'] = 'i18n_translation_node_overview';
$items['node/%node/translate']['file'] = 'i18n.pages.inc';
$items['node/%node/translate']['module'] = 'i18n';
*/
/**
$items = array();
$items['node/%node/translate'] = array(
'title' => 'Translate',
'page callback' => 'translation_node_overview',
'page arguments' => array(1),
'access callback' => '_translation_tab_access',
'access arguments' => array(1),
'type' => MENU_LOCAL_TASK,
'weight' => 2,
'file' => 'i18n.pages.inc',
);
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_alter_translation_link().
*
* Handles links for extended language. The links will have current language
*/
function i18n_translation_link_alter(&$links, $path) {
global $language;
// Check for a node related path, and for its translations.
if ((preg_match("!^node/([0-9]+)(/.+|)$!", $path, $matches)) && ($node = node_load((int)$matches[1])) && !empty($node->tnid)) {
$languages = language_list();
$extended = array();
foreach (translation_node_get_translations($node->tnid) as $langcode => $translation_node) {
if (!isset($links[$langcode]) && isset($languages[$langcode])) {
$extended[$langcode] = array(
'href' => 'node/'.$translation_node->nid . $matches[2],
'language' => $language,
'language_icon' => $languages[$langcode],
'title' => $languages[$langcode]->native,
'attributes' => array('class' => 'language-link'),
);
}
}
// This will run after languageicon module, so we add icon in case that one is enabled
if ($extended && function_exists('languageicons_translation_link_alter')) {
languageicons_translation_link_alter($extended, $path);
}
$links = array_merge($links, $extended);
}
}
/**
* Implementation of hook_link_alter().
*
* Handles links for extended languages. Sets current interface language;
*/
function i18n_link_alter(&$links, $node) {
global $language;
if ($node->tnid) {
foreach (array_keys(i18n_language_list('extended')) as $langcode) {
$index = 'node_translation_'.$langcode;
if (!empty($links[$index])) {
$links[$index]['language'] = $language;
}
}
}
}
/**
* 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);
}
}
/**
* Simple i18n API
*/
/**
* Get language properties
*
* @param $code
* Language code
* @param $property
* It may be 'name', 'native', 'ltr'...
*/
function i18n_language_property($code, $property) {
$languages = language_list();
return isset($languages[$code][$property]) ? $languages[$code][$property] : NULL;
}
/**
* Get node language
*/
function i18n_node_get_lang($nid, $default = '') {
$lang = db_result(db_query('SELECT language FROM {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'
*
* @param $node
* Node to retrieve language list for
* @param $translate
* Only languages available for translation. Filter out existing translations
*/
function i18n_node_language_list($node, $translate = FALSE) {
// Check if the node module manages its own language list
$languages = node_invoke($node, 'language_list', $translate);
if (!$languages) {
if (variable_get('i18n_node_'.$node->type, 0) == LANGUAGE_SUPPORT_EXTENDED) {
$languages = locale_language_list('name', TRUE); // All defined languages
} else {
$languages = locale_language_list(); // All enabled languages
}
if ($translate && isset($node->tnid) && $node->tnid && ($translations = translation_node_get_translations($node->tnid))) {
unset($translations[$node->language]);
foreach (array_keys($translations) as $langcode) {
unset($languages[$langcode]);
}
}
$languages = array('' => t('Language neutral')) + $languages;
}
return $languages;
}
/**
* 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 == variable_get('site_frontpage', 'node')) {
$path = '';
}
$names = $names ? $names : locale_language_list('native');
foreach (array_keys(i18n_supported_languages()) as $lang){
$links[$lang]= theme('i18n_link', $names[$lang], i18n_path($path, $lang), $lang, $query);
}
return $links;
}
/**
* Selection mode for content.
*
* Warning: when used with params they need to be escaped, as some values are thrown directly in queries
*
* 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 = NULL;
static $current_value = '';
static $store = array();
// Initialization, first time this runs with no explicit mode
if (!$current_mode && !$mode) {
$current_mode = variable_get('i18n_selection_mode', 'simple');
if ($current_mode != 'off') {
// Node language when loading specific nodes or creating translations
if (arg(0) == 'node' ) {
if (is_numeric(arg(1)) && ($node = node_load(arg(1))) && $node->language ) {
$current_mode = 'node';
$current_value = $node->language;
} elseif (arg(1) == 'add' && !empty($_GET['translation']) && !empty($_GET['language']) && empty($_POST)) {
$current_mode = 'translation';
$current_value = db_escape_string($_GET['language']);
}
} elseif (arg(0) == 'admin') {
// There are some exceptions for admin pages
if (arg(1) == 'content' && user_access('administer all languages')) {
// No restrictions for administration pages
$current_mode = 'off';
} elseif (arg(1) == 'build' && arg(2) == 'menu-customize') {
// All nodes available when editing custom menu items
$current_mode = 'off';
}
}
}
}
if(!$mode) {
return $current_mode;
} elseif($mode == 'params'){
return $current_value;
} elseif($mode == 'reset'){
list($current_mode, $current_value) = array_pop($store);
} else {
array_push($store, array($current_mode, $current_value));
$current_mode = $mode;
$current_value = $params;
}
}
/**
* Implementation of hook_db_rewrite_sql()
*
* Rewrite node queries so language selection options are enforced
*/
function i18n_db_rewrite_sql($query, $primary_table, $primary_key, $args = array()){
// If mode is 'off' = no rewrite, we cannot return any empty 'where' string so check here
$mode = i18n_selection_mode();
if ($mode == 'off') return;
// Disable language conditions for views
if (array_key_exists('view',$args)) return;
switch ($primary_table) {
case 'n':
case 'node':
// No rewrite for queries with subselect ? (views count queries)
// @ TO DO Actually these queries look un-rewrittable, check with other developers
if (preg_match("/FROM \(SELECT/", $query)) return;
// No rewrite for translation module queries
if (preg_match("/.*FROM {node} $primary_table WHERE.*$primary_table\.tnid/", $query)) return;
// When loading specific nodes, language conditions shouldn't apply
if (preg_match("/WHERE.*\s$primary_table.nid\s*=\s*(\d|%d)/", $query)) return;
// If language conditions already there, get out
if (preg_match("/i18n/", $query)) return;
// Mixed mode is a bit more complex, we need to join in one more table
// and add some more conditions, but only if language is not default
if ($mode == 'mixed' && i18n_get_lang() != i18n_default_language()) {
$result['join'] = "LEFT JOIN {node} i18n ON $primary_table.tnid = i18n.tnid AND i18n.language = '".i18n_get_lang()."'";
$result['where'] = i18n_db_rewrite_where($primary_table, 'node', 'simple');
// So we show also nodes that have default language
$result['where'] .= " OR ($primary_table.language = '" . i18n_default_language() . "' AND i18n.nid IS NULL)" ;
} else {
$result['where'] = i18n_db_rewrite_where($primary_table, 'node', $mode);
}
return $result;
}
}
/**
* Rewrites queries depending on rewriting mode
*/
function i18n_db_rewrite_where($alias, $type, $mode = NULL){
if (!$mode) {
// Some exceptions for query rewrites
$mode = i18n_selection_mode();
}
// Get languages to simplify query building
$current = i18n_get_lang();
$default = i18n_default_language();
if ($mode == 'strict' && $type != 'node') {
// Special case. Selection mode is 'strict' but this should be only for node queries
$mode = 'simple';
}
elseif ($mode == 'mixed' && $current == $default) {
// If mode is mixed but current = default, is the same as 'simple'
$mode = 'simple';
}
switch ($mode){
case 'off':
return '';
case 'simple':
return "$alias.language ='$current' OR $alias.language ='' OR $alias.language IS NULL" ;
case 'mixed':
return "$alias.language ='$current' OR $alias.language ='$default' OR $alias.language ='' OR $alias.language IS NULL" ;
case 'strict':
return "$alias.language ='$current'" ;
case 'node':
case 'translation':
return "$alias.language ='".i18n_selection_mode('params')."' OR $alias.language ='' OR $alias.language IS NULL" ;
case 'default':
return "$alias.language ='$default' 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
*/
function i18n_form_alter(&$form, $form_state, $form_id) {
switch ($form_id){
case 'node_type_form':
// Add extended language support option to content type form.
$form['workflow']['i18n_node'] = array(
'#type' => 'radios',
'#title' => t('Extended language support'),
'#default_value' => variable_get('i18n_node_'. $form['#node_type']->type, LANGUAGE_SUPPORT_NORMAL),
'#options' => _i18n_content_language_options(),
'#description' => t('If enabled, all defined languages will be allowed for this content type in addition to only enabled ones. This is useful to have more languages for content than for the interface.')
);
break;
default:
// Extended language for node edit form
if (isset($form['#id']) && $form['#id'] == 'node-form') {
if (isset($form['#node']->type) && variable_get('language_content_type_'. $form['#node']->type, 0)) {
$form['language']['#options'] = i18n_node_language_list($form['#node'], TRUE);
}
}
/** @ TO DO Upgrade
if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id && $node = $form['#node']) {
// Language field
if(variable_get('i18n_node_'.$form['type']['#value'], 0) && !isset($form['i18n']['language'])) {
// Language field
$form['i18n'] = array('#type' => 'fieldset', '#title' => t('Multilingual settings'), '#collapsible' => TRUE, '#collapsed' => FALSE, '#weight' => -4);
// Language will default to current only when creating a node
$language = isset($form['#node']->language) ? $form['#node']->language : (arg(1)=='add' ? i18n_get_lang() : '');
$form['i18n']['language'] = _i18n_language_select($language, t('If you change the language, you must click on Preview to get the right categories & terms for that language.'), -4, i18n_node_language_list($node));
$form['i18n']['trid'] = array('#type' => 'value', '#value' => $form['#node']->trid);
}
// 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($node->path) {
$alias = drupal_lookup_path('alias', 'node/'.$node->nid);
if($alias && $alias != 'node/'.$node->nid){
$form['#node']->path = $alias;
} else {
unset($form['#node']->path);
}
}
}
*/
// Multilingual variables in settings form
if (isset($form['#theme']) && $form['#theme'] == 'system_settings_form' && $variables = variable_get('i18n_variables', 0)) {
if (i18n_form_alter_settings($form, $variables)) {
$form['#submit'][] = 'i18n_variable_form_submit';
}
}
}
}
/**
* Implementation of hook_perm().
*
* Permissions defined
* - administer all languages
* Disables language conditions for administration pages, so the user can view objects for all languages at the same time.
* This applies for: menu items, taxonomy
*/
function i18n_perm() {
return array('administer all languages');
}
/**
* Process menu and menu item add/edit form submissions.
*/
function i18n_menu_edit_item_form_submit($form_id, $form_values) {
$mid = menu_edit_item_save($form_values);
db_query("UPDATE {menu} SET language = '%s' WHERE mid = %d", $form_values['language'], $mid);
return 'admin/build/menu';
}
/**
* Check for multilingual variables in form
*/
function i18n_form_alter_settings(&$form, &$variables) {
$result = 0;
foreach (element_children($form) as $field) {
if(isset($form[$field]['#type']) && $form[$field]['#type'] == 'fieldset') {
$result += i18n_form_alter_settings($form[$field], $variables);
}
elseif (in_array($field, $variables)) {
$form[$field]['#description'] .= ' '.t('This is a multilingual variable.').'';
$result++;
}
}
return $result;
}
/**
* Save multilingual variables and remove them from form
*/
function i18n_variable_form_submit($form, &$form_state) {
$form_values = $form_state['values'];
$op = isset($form_values['op']) ? $form_values['op'] : '';
$variables = variable_get('i18n_variables', array());
$language = i18n_get_lang();
foreach ($form_values as $key => $value) {
if (in_array($key, $variables)) {
if ($op == t('Reset to defaults')) {
i18n_variable_del($key, $language);
}
else {
if (is_array($value) && isset($form_values['array_filter'])) {
$value = array_keys(array_filter($value));
}
i18n_variable_set($key, $value, $language);
}
unset($form_values[$key]);
}
}
// Re-submit form
// system_settings_form_submit($form_id, $form_values);
}
/**
* Initialization of multilingual variables
*
* @param $language
* Language to retrieve variables. Defaults to current language
* @param $prefix
* Variable name prefix to load just a selected group of variables
*/
function i18n_variable_init($langcode = NULL, $prefix = ''){
global $conf;
global $i18n_conf;
$langcode = $langcode ? $langcode : i18n_get_lang();
if ($i18n_variables = variable_get('i18n_variables', '')){
if (!$i18n_conf) {
$i18n_conf = array();
}
$variables = _i18n_variable_init($langcode, $prefix);
foreach($i18n_variables as $name){
$i18n_conf[$name] = isset($variables[$name]) ? $variables[$name] : (isset($conf[$name]) ? $conf[$name] : '');
}
$conf = array_merge($conf, $i18n_conf);
}
}
/**
* Helper function to create language selector
*/
function _i18n_language_select($value ='', $description ='', $weight = -20, $languages = NULL){
$languages = $languages ? $languages : locale_language_list();
return array(
'#type' => 'select',
'#title' => t('Language'),
'#default_value' => $value,
'#options' => array_merge(array('' => ''), $languages),
'#description' => $description,
'#weight' => $weight,
);
}
/**
* Load language variables into array
*/
function _i18n_variable_init($language, $prefix = ''){
$variables = array();
$cacheid = 'variables:'.$language.($prefix ? ':'.$prefix : '');
if ($cached = cache_get($cacheid)) {
$variables = $cached->data;
}
else {
$result = db_query("SELECT * FROM {i18n_variable} WHERE language='%s' AND name LIKE '%s%'", $language, $prefix);
while ($variable = db_fetch_object($result)) {
$variables[$variable->name] = unserialize($variable->value);
}
cache_set($cacheid, $variables);
}
return $variables;
}
/**
* Save multilingual variables that may have been changed by other methods than settings pages
*/
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));
}
}
}
/**
* Check whether we are in bootstrap mode
*/
function _i18n_is_bootstrap(){
return !function_exists('drupal_get_headers');
}
/**
* Drupal 6, backwards compatibility layer
* @ TO DO Fully upgrade all the modules and remove
*/
/**
* This one expects to be called first from common.inc
*/
function i18n_get_lang() {
global $language;
return $language->language;
}
/**
* @defgroup i18n_api Extended language API
* @{
* This is an extended language API to be used by modules in i18n package.
*/
/**
* Returns language lists
*/
function i18n_language_list($type = 'enabled', $field = 'name') {
switch ($type) {
case 'enabled':
return locale_language_list($field);
case 'extended':
$enabled = locale_language_list($field);
$defined = locale_language_list($field, TRUE);
return array_diff_assoc($defined, $enabled);
}
}
/**
* Returns default language code
*/
function i18n_default_language(){
return language_default('language');
}
/**
* Get list of supported languages, native name
*
* @param $all
* TRUE to get all defined languages
*/
function i18n_supported_languages($all = FALSE) {
return locale_language_list('native', $all);
}
/**
* Set a persistent language dependent variable.
*
* @param $name
* The name of the variable to set.
* @param $value
* The value to set. This can be any PHP data type; these functions take care
* of serialization as necessary.
* @param $langcode
* Language code
*/
function i18n_variable_set($name, $value, $langcode) {
global $conf, $i18n_conf;
db_lock_table('i18n_variable');
db_query("DELETE FROM {i18n_variable} WHERE name = '%s' AND language='%s'", $name, $langcode);
db_query("INSERT INTO {i18n_variable} (name, language, value) VALUES ('%s', '%s', '%s')", $name, $langcode, serialize($value));
db_unlock_tables();
cache_clear_all('variables:'.$langcode, 'cache');
$conf[$name] = $value;
$i18n_conf[$name] = $value;
}
/**
* Unset a persistent multilingual variable.
*
* @param $name
* The name of the variable to undefine.
* @param $langcode
* Language code
*/
function i18n_variable_del($name, $langcode) {
global $conf, $i18n_conf;
db_query("DELETE FROM {i18n_variable} WHERE name = '%s' AND language='%s'", $name, $langcode);
cache_clear_all('variables:'.$langcode, 'cache');
unset($conf[$name]);
unset($i18n_conf[$name]);
}
/**
* Utility. Get part of array variable
*/
function i18n_array_variable_get($name, $element, $default = NULL) {
if (($values = variable_get($name, array())) && isset($values[$element])) {
return $values[$element];
} else {
return $default;
}
}
/**
* Utility. Set part of array variable
*/
function i18n_array_variable_set($name, $element, $value) {
$values = variable_get($name, array());
$values[$element] = $value;
variable_set($name, $values);
}
/**
* @} End of "defgroup i18n_api".
*/
// List of language support modes for content
function _i18n_content_language_options() {
return array(
LANGUAGE_SUPPORT_NORMAL => t('Normal - All enabled languages will be allowed.'),
LANGUAGE_SUPPORT_EXTENDED => t('Extended - All defined languages will be allowed.')
);
}