diff --git a/INSTALL.txt b/INSTALL.txt index 3d3c11f727168d5c9779dc686c9e0191d0f1baa9..83dfeb69f4922093f8d8e787382e4751f8f866af 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -3,10 +3,9 @@ ******************************************************************** Name: i18n module and translation module Author: Jose A. Reyero -Email: drupal at reyero dot net ******************************************************************** - This is the 5.x version of i18n module, and works with Drupal 5.x + This is the 6.x version of i18n module, and works with Drupal 6.x ******************************************************************** ******************************************************************** @@ -29,41 +28,10 @@ POST-INSTALLATION/CONFIGURATION: - Configure the modules in Administer > Site Configuration > Multilingual system - You might get an error message about languages not being enabled yet. - Visit the suggested page (Manage Languages) and enable the languages you need. - In case you find out they're already enabled, just press the button "save configuration". - -- Enable multilingual support for each content type you want to translate - administer > content management > content types > edit (for each type separately) - - A new field 'language' and a new tab 'translations' will be available when editing these node types - -- There are now two language blocks - - A simple language switcher provided by i18n.module - - A translation block provided by translation.module - - Set up language for existing nodes, editing them manually. - Set up language for existing taxonomy terms Only terms in the node's language or terms without language will show up when editing a node -Language Definition [NEW for Drupal 5] -=================== -RECOMMENDED: You can use the languages defined -and enabled- in 'locale' module - -ADVANCED, EXPERTS ONLY: -Modify the Drupal configuration file '../settings.php' to define the languages you want to use. - For instance, for English, Spanish: - - $conf['i18n_languages'] = array( - 'site_default' => 'en', // Default language - 'name' => array('es' => "Spanish", 'en' => 'English'), // English names for languages - 'native' => array('es' => "Español", 'en' => 'English'), // Native names - 'enabled' => array('es' => TRUE, 'en' => TRUE), // Languages enabled - // 'rtl' => array('es' => TRUE), // This only if Spanish were an RTL language, which is not - ); - -* This will override languages defined by locale module - Language dependent variables ============================ The list of variables to be made language dependent must be defined in the config file: diff --git a/README.txt b/README.txt index 503587ff7ca5856b38b4a7097a3fee62995e53b4..a2289ceb156a46ee59684469d913a0380e0444f9 100644 --- a/README.txt +++ b/README.txt @@ -2,59 +2,32 @@ README.txt ========== ******************************************************************** -This is i18n package, development version , and works with Drupal 5.x +This is i18n package, development version , and works with Drupal 6.x ******************************************************************** WARNING: DO READ THE INSTALL FILE ******************************************************************** Updated documentation will be kept on-line at http://drupal.org/node/67817 ******************************************************************** -This is not a single module anymore but a collection of modules providing complementary features - i18n --> basic module which will add language to nodes, vocabularies and terms - translation --> module to add translation relationships - -******************************************************************** -These modules provide support for multilingual Drupal sites: - * Multilingual content, some basic translation interface, and links between translated versions - * Translation of the user interface for registered and anonymous users (with locale) - * Detection of the brower language - * Keeps the language settings accross consecutive requests using URL rewriting. - * Provides a block for language switching -i18n.module- and one for translations -translation.module- -Multilingual content: -===================== -Multilingual content means providing content translated to different languages or language specific content, which is not the same as interface translation. Interface translation is done through Drupal's localization system. -This module supports: - - Multilingual nodes - - Multilingual taxonomy vocabularies and terms - - Translations for nodes and terms +This is a collection of modules providing multilingual features. +These modules will build onto Drupal 6 core features enabling a full multilingual site -When you navigate the site using multiple languages, the pages will just show terms and nodes for the chosen language plus the ones that haven't a definde language. -When editing a node, you must click on 'Preview' after changing language for the right vocabularies and terms to be shown. +i18n: core i18n functionality +============================= +- Multilingual variables +- Extended language API for other modules -The multi language support is expected to work for all node types, and node listings +languageicons: language icons for translation links +============= +This module will be spin-off from the main package -Taxonomy translation: +i18ntaxonomy: Taxonomy translation: ==================== You can create vocabularies and terms with or without language. - If you set language for a vocabulary/term, that term will just show up for pages in that language - If you set language for a vocabulary, all the terms in that vocabulary will be assigned that language. - When editing nodes, if you change the language for a node, you have to click on 'Preview' to have the right vocabularies/terms for that language. Otherwise, the language/taxonomy data for that node could be inconsistent. -About URL aliasing with language codes -requires path module -============================================================ -Incoming URL's are now translated following these steps: -1. First, a translation is searched for path with language code: 'en/mypage' -2. If not found, language code is removed, and path translation is searched again: 'mypage' - -Thus, you can define aliases with or without language codes in them. - -This language code will be taken from browser if enabled 'Browser language detection, or will be the default otherwise. - -To have aliases for a translated node/page, you have to define each of them. I.e.: - en/mycustompath -> node/34 (which is suppossed to be the english version) - es/mycustompath -> node/35 (which should be the spanish version) - -For outgoing URL's, the language code will be added automatically. About language dependent variables: ================================== diff --git a/i18n.info b/i18n.info index f2ea38ec8e2a55646d94fbe19297dfd89519e7a6..a41cb1773de2c33ca596b38f5ea0d96c28648e75 100644 --- a/i18n.info +++ b/i18n.info @@ -2,4 +2,5 @@ name = Internationalization description = Enables multilingual content. dependencies = locale package = Multilanguage - i18n - +version = VERSION +core = 6.x \ No newline at end of file diff --git a/i18n.install b/i18n.install index 0ad080d3f905908cb2c0e59e744a91d3e0a9d49e..33d6603f499459488249de6322fb5d88388939f5 100644 --- a/i18n.install +++ b/i18n.install @@ -9,30 +9,25 @@ function i18n_install() { switch ($GLOBALS['db_type']) { case 'mysql': case 'mysqli': + /** db_query("CREATE TABLE {i18n_node} ( nid int(11) unsigned NOT NULL , trid int(10) unsigned NOT NULL default '0', language VARCHAR(12) NOT NULL default '', status smallint(6) NOT NULL default '0', PRIMARY KEY (nid) - )"); - db_query("ALTER TABLE {term_data} - ADD language varchar(12) NOT NULL default '', - ADD trid int(10) unsigned NOT NULL default '0' - "); - db_query("ALTER TABLE {vocabulary} - ADD language varchar(12) NOT NULL default '' - "); + ) "); + */ db_query("CREATE TABLE {i18n_variable} ( name varchar(48) NOT NULL default '', language varchar(12) NOT NULL default '', value longtext NOT NULL, PRIMARY KEY (name, language) - )"); - + ) /*!40100 DEFAULT CHARACTER SET UTF8 */ "); break; case 'pgsql': + /* db_query("CREATE TABLE {i18n_node} ( nid integer NOT NULL default '0', trid integer NOT NULL default '0', @@ -40,31 +35,16 @@ function i18n_install() { status smallint NOT NULL default '0', PRIMARY KEY (nid) )"); + */ db_query("CREATE TABLE {i18n_variable} ( name varchar(48) NOT NULL default '', language varchar(12) NOT NULL default '', value text NOT NULL, PRIMARY KEY (name, language) )"); - db_query("ALTER TABLE {term_data} ADD language varchar(12)"); - db_query("UPDATE {term_data} SET language=''"); - db_query("ALTER TABLE {term_data} ALTER COLUMN language SET NOT NULL"); - db_query("ALTER TABLE {term_data} ALTER COLUMN language SET DEFAULT ''"); - - db_query("ALTER TABLE {term_data} ADD trid integer"); - db_query("UPDATE {term_data} SET trid='0'"); - db_query("ALTER TABLE {term_data} ALTER COLUMN trid SET NOT NULL"); - db_query("ALTER TABLE {term_data} ALTER COLUMN trid SET DEFAULT '0'"); - - db_query("ALTER TABLE {vocabulary} ADD language varchar(12)"); - db_query("UPDATE {vocabulary} SET language=''"); - db_query("ALTER TABLE {vocabulary} ALTER COLUMN language SET NOT NULL"); - db_query("ALTER TABLE {vocabulary} ALTER COLUMN language SET DEFAULT ''"); - - // Sequences - db_query("CREATE SEQUENCE {i18n_node}_trid_seq INCREMENT 1 START 1"); - db_query("CREATE SEQUENCE {term_data}_trid_seq INCREMENT 1 START 1"); - } + } + // Set module weight for it to run after core modules + db_query("UPDATE {system} SET weight = -10 WHERE name = 'i18n' AND type = 'module'"); } /** @@ -101,4 +81,31 @@ function i18n_update_4(){ )"); return $items; } -?> \ No newline at end of file + +/** + * Drupal 5 updates + */ +// Multilingual menu items +function i18n_update_5(){ + $items[] = update_sql("ALTER TABLE {menu} ADD language VARCHAR(12) NOT NULL default ''"); + return $items; +} +// Module weithgts +function i18n_update_6(){ + // Old module weights. Caused some trouble with other modules. +} +// Redefinition of module weights +function i18n_update_7(){ + $items[] = update_sql("UPDATE {system} SET weight = -10 WHERE name = 'i18n' AND type = 'module'"); + $items[] = update_sql("UPDATE {system} SET weight = 10 WHERE name = 'translation' AND type = 'module'"); + return $items; +} +// Update tables to utf8 +function i18n_update_8(){ + return _system_update_utf8(array('i18n_variable')); +} + +/** + * Drupal 6 updates + */ + // Move node language and trid into node table \ No newline at end of file diff --git a/i18n.module b/i18n.module index 04ea0b62fbda1a839b4b33de7a742f5f90326273..05876bdf9b05c138c795d0c5d817400c5e609be8 100644 --- a/i18n.module +++ b/i18n.module @@ -2,41 +2,32 @@ // $Id$ /** + * @file * Internationalization (i18n) module * + * Trimmed down + * - Language initialization + * * @author Jose A. Reyero, 2004 * */ -/** - * Module initialization - * - * Get language from path if exists and Initialize i18n system - */ +// Some constants. Language support modes for content +define('LANGUAGE_SUPPORT_NONE', 0); +define('LANGUAGE_SUPPORT_NORMAL', 1); +define('LANGUAGE_SUPPORT_EXTENDED', 2); /** - * This one expects to be called first from common.inc + * Implementation of hook_theme() */ -function i18n_get_lang($setlanguage = NULL) { - static $i18n_language; - //see if the language is already set. - if ($setlanguage) { - $i18n_language = $setlanguage; - } elseif ($i18n_language) { - return $i18n_language; - } else { - _i18n_init(); - return $i18n_language = _i18n_get_lang(); - } +function i18n_theme() { + return array( + 'i18n_language_icon' => array( + 'arguments' => array('langcode' => array()), + ), + ); } -/** - * Minimum initialization - */ -function _i18n_init(){ - global $i18n_langpath; - $path = _i18n_get_original_path(); - $i18n_langpath = i18n_get_lang_prefix($path); -} + /** * Language block @@ -58,42 +49,27 @@ function i18n_block($op = 'list', $delta = 0) { /** * 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() + * Will initialize language dependent variables + * Modify rewriting conditions when viewing specific nodes */ function i18n_init(){ - 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 - // 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 if(!_i18n_is_bootstrap()){ //include drupal_get_path('module', 'i18n').'/i18n.inc'; i18n_variable_init(); - } + } + if (arg(0) == 'node') { + if(isset($_POST['language']) && $_POST['language']) { + $pagelang = $_POST['language']; + } elseif( is_numeric(arg(1)) && $node = node_load(arg(1)) ) { + // Node language when loading specific nodes + $pagelang = $node->language; + } + if(isset($pagelang)) i18n_selection_mode('node', db_escape_string($pagelang)); + } elseif(arg(0) == 'admin' && arg(0) == 'content' && user_access('administer all languages')) { + // No restrictions for administration pages + i18n_selection_mode('off'); + } } /** @@ -106,11 +82,11 @@ function i18n_help($section = 'admin/help#i18n' ) { $output .= ''; $output .= '

'. t('For more information please read the on-line help pages.', array('@i18n' =>'http://drupal.org/node/31631')) .'

'; return $output; @@ -122,43 +98,21 @@ function i18n_help($section = 'admin/help#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('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['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'); - } - } +function i18n_menu() { + $items[] = array( + 'path' => 'admin/settings/i18n', + 'title' => 'Multilingual system', + 'description' => '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' => 'Internationalization', + 'type' => MENU_DEFAULT_LOCAL_TASK + ); return $items; } @@ -166,6 +120,7 @@ function i18n_menu($may_cache) { /** * Implementation of hook_nodeapi(). */ +/* function i18n_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { if (variable_get("i18n_node_$node->type", 0)) { switch ($op) { @@ -174,102 +129,88 @@ function i18n_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { case 'insert': case 'update': db_query("DELETE FROM {i18n_node} WHERE nid=%d",$node->nid); - if($node->language){ + 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 + * Implementation of hook_user() * - * $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; + * 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. * + * TO DO: Add exclude paths for content selection * Some options have been removed from previous versions: * - Languages are now taken from locale module unless defined in settings file * - Language dependent tables are authomatically used if defined in settings file */ function i18n_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".'), - ); - // Advanced options - $form['advanced'] = array( + // Content selection options + $form['selection'] = array( '#type' => 'fieldset', - '#title' => t('Advanced settings'), - '#collapsible' => TRUE, - '#collapsed' => TRUE, + '#title' => t('Content selection'), + //'#collapsible' => TRUE, + //'#collapsed' => TRUE, ); - $form['advanced']['i18n_selection_mode'] = array( + $form['selection']['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); } @@ -278,30 +219,9 @@ function i18n_admin_settings() { * Simple i18n API */ -/** - * Get list of supported languages - */ -function i18n_supported_languages() { - return i18n_languages('active'); -} -/** - * Returns default language - */ -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 @@ -312,7 +232,7 @@ function i18n_language_rtl($language = NULL) { * It may be 'name', 'native', 'ltr'... */ function i18n_language_property($code, $property = 'native') { - $languages = i18n_languages($property); + $languages = language_list($property); return $languages[$code]; } @@ -332,228 +252,80 @@ function i18n_languages($key = 'active') { if($languages = variable_get('i18n_languages', 0)) { foreach($languages['name'] as $code => $name) { if($languages['enabled'][$code]) { - $languages['active'][$code] = $languages['native'][$code]; + $languages['active'][$code] = $name; } } } else { - $languages = locale_supported_languages(); - $languages['site_default'] = key($languages); + // 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 - */ -function i18n_get_browser_lang() { - $languages = i18n_supported_languages(); - $exploded_server = explode(";", $_SERVER["HTTP_ACCEPT_LANGUAGE"]); - $accept=explode(',', array_shift($exploded_server)); - foreach ($accept as $lang) { - if(empty($lang)) { - continue; - } elseif (array_key_exists($lang, $languages)) { - return $lang; - } elseif (array_key_exists(substr($lang, 0, 2), $languages)) { - return substr($lang, 0, 2); - } - } -} - -/** - * Get language code from path. - * - * @param $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; - } -} - -/** - * 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')); -} - -/** - * 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 + * Get node 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)); + $lang = db_result(db_query('SELECT language FROM {node} WHERE nid=%d',$nid)); return $lang ? $lang : $default ; } /** - * Returns main language, two letter code + * Get allowed languages for node + * + * This allows node types to define its own language list implementing hook 'language_list' */ -function i18n_get_main_lang($lang = NULL){ - $lang = $lang ? $lang : i18n_get_lang(); - return substr($lang, 2); +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 + } } /** * 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) { +function i18n_get_links($path = '', $query = NULL, $names = NULL) { if($path == i18n_frontpage()) { $path = ''; } - foreach(i18n_supported_languages() as $lang => $name){ - $links[$lang]= theme('i18n_link', $name, i18n_path($path, $lang), $lang, $query); + $names = $names ? $names : i18n_languages('native'); + foreach (array_keys(i18n_supported_languages()) as $lang){ + $links[$lang]= theme('i18n_link', $names[$lang], i18n_path($path, $lang), $lang, $query); } 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. - if ($i18n_lang) { - return $i18n_lang; - } - // 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'; - } - drupal_goto($lang); -} - /** * i18n_selection_mode * @@ -586,7 +358,7 @@ function i18n_selection_mode($mode= NULL, $params= NULL){ $current_value = $params; } } - +// List of selection modes function _i18n_selection_mode(){ return array( 'simple' => t('Only current language and no language'), @@ -596,6 +368,14 @@ function _i18n_selection_mode(){ '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 @@ -615,100 +395,46 @@ function theme_i18n_link($text, $target, $lang, $query= NULL, $fragment = NULL){ 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 ""; - } -} - /* @} */ -/** - * 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); - } -} - -/** - * Rewrites path with current language - */ -function i18n_url_rewrite($type, $path, $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); + // Node queries. + // No rewrite for translation module queries + if (preg_match("/.*FROM {node} WHERE.* 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; + + //$result['join'] = "LEFT JOIN {i18n_node} i18n ON $primary_table.nid = i18n.nid"; + $result['where'] = i18n_db_rewrite_where($primary_table, 'node'); + return $result; } } -/** - * Rewrite node queries - */ -function i18n_db_node_rewrite($query, $primary_table, $primary_key, $mode){ - // When loading specific nodes, language conditions shouldn't apply - if (preg_match("/WHERE.*\s$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; -} - -/** - * Rewrite taxonomy queries - */ -function i18n_db_taxonomy_rewrite($query, $primary_table, $primary_key, $mode){ - // 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 - if (preg_match("/WHERE r\.nid = \%d/", $query)) return; - - $result['where'] = i18n_db_rewrite_where($primary_table, $mode); - - return $result; -} - /** * Rewrites queries depending on rewriting mode */ -function i18n_db_rewrite_where($alias, $mode){ +function i18n_db_rewrite_where($alias, $type, $mode = NULL){ + if (!$mode) { + // Some exceptions for query rewrites + $mode = i18n_selection_mode(); + // drupal_set_message("i18n_db_rewrite mode=$mode query=$query"); + } + // Special case. Selection mode is 'strict' but this should be only for node queries + if ($mode == 'strict' && $type != 'node') { + $mode = 'simple'; + } switch($mode){ + case 'off': + return ''; case 'simple': return "$alias.language ='".i18n_get_lang()."' OR $alias.language ='' OR $alias.language IS NULL" ; case 'mixed': @@ -721,7 +447,7 @@ function i18n_db_rewrite_where($alias, $mode){ 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')); + return str_replace('%alias', $alias, i18n_selection_mode('params')); } } @@ -737,96 +463,79 @@ function i18n_exit(){ * * This is the place to add language fields to all forms */ -function i18n_form_alter($form_id, &$form) { - //drupal_set_message("i18n_form_alter form_id=$form_id "); +function i18n_form_alter(&$form, $form_state, $form_id) { + //drupal_set_message("DEBUG: 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'].' ('.$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( + // Build a fieldset to allow some more options here + $form['workflow']['i18n'] = array( + '#type' => 'fieldset', + '#title' => t('Multilingual options'), + ); + $form['workflow']['i18n']['i18n_node'] = array( '#type' => 'radios', - '#title' => t('Multilingual support'), + '#title' => t('Multilingual content'), '#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.'), + '#options' => _i18n_content_languages(), + '#description' => t('Enables language field and translations for this content type.'), ); break; - case '_locale_admin_manage_screen': // Locale language settings - $languages = locale_supported_languages(TRUE, TRUE); - $i18nlangs = variable_get('i18n_languages', array()); - $form['native'] = array('#tree' => TRUE); - foreach ($languages['name'] as $key => $lang) { - $options[$key] = ''; - - $form['native'][$key] = array('#type' => 'textfield', - '#default_value' => $i18nlangs['native'][$key] ? $i18nlangs['native'][$key] : $lang, - '#size' => 15, - '#maxlength' => 64, - ); - } - $form['rtl'] = array( - '#type' => 'checkboxes', - '#options' => $options, - '#default_value' => $i18nlangs['rtl'] - ); - $form['#base'] = 'i18n_admin_manage_screen'; - if(!is_array($form['#submit'])) { - $form['#submit'] = array('_locale_admin_manage_screen_submit' => array()); - } - $form['#submit'] = array('i18n_admin_manage_screen_submit' => array()) + $form['#submit']; - break; - + default: // Node edit form - if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) { + /** @ 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 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); + $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($form['#node']->path) { - $alias = drupal_lookup_path('alias', 'node/'.$form['#node']->nid); - if($alias && $alias != 'node/'.$form['#node']->nid){ + // 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 ($form['#base'] == 'system_settings_form' && $variables = variable_get('i18n_variables', 0)) { - if(i18n_form_alter_settings($form, $variables)) { - $form['#submit'] = array('i18n_variable_form_submit' => array()); + 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'; +} + /** * Theme the locale admin manager form. */ @@ -859,13 +568,14 @@ function i18n_admin_manage_screen_submit($form_id, $form_values) { } variable_set('i18n_languages', $save); } + /** * Check for multilingual variables in form */ function i18n_form_alter_settings(&$form, &$variables) { $result = 0; - foreach(element_children($form) as $field) { - if($form[$field]['#type'] == 'fieldset') { + 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)) { @@ -879,12 +589,13 @@ function i18n_form_alter_settings(&$form, &$variables) { /** * Save multilingual variables and remove them from form */ -function i18n_variable_form_submit($form_id, $form_values) { +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 (in_array($key, $variables)) { if ($op == t('Reset to defaults')) { i18n_variable_del($key, $language); } @@ -898,19 +609,26 @@ function i18n_variable_form_submit($form_id, $form_values) { } } // Re-submit form - system_settings_form_submit($form_id, $form_values); + // system_settings_form_submit($form_id, $form_values); } /** - * Initialization of multilingual variables + * 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(){ +function i18n_variable_init($language = NULL, $prefix = ''){ global $conf; global $i18n_conf; - $lang = _i18n_get_lang(); - if($i18n_variables = variable_get('i18n_variables', '')){ - $i18n_conf = array(); - $variables = _i18n_variable_init(); + $language = $language ? $language : i18n_get_lang(); + if ($i18n_variables = variable_get('i18n_variables', '')){ + if (!$i18n_conf) { + $i18n_conf = array(); + } + $variables = _i18n_variable_init($language, $prefix); foreach($i18n_variables as $name){ $i18n_conf[$name] = isset($variables[$name]) ? $variables[$name] : (isset($conf[$name]) ? $conf[$name] : ''); } @@ -918,75 +636,40 @@ function i18n_variable_init(){ } } -/** - * 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. - */ -function i18n_variable_set($name, $value, $language) { - global $conf, $i18n_conf; - - db_lock_table('i18n_variable'); - db_query("DELETE FROM {i18n_variable} WHERE name = '%s' AND language='%s'", $name, $language); - db_query("INSERT INTO {i18n_variable} (name, language, value) VALUES ('%s', '%s', '%s')", $name, $language, serialize($value)); - db_unlock_tables(); - cache_clear_all('variables:'.$language, 'cache'); - - $conf[$name] = $value; - $i18n_conf[$name] = $value; -} - -/** - * Unset a persistent multilingual variable. - * - * @param $name - * The name of the variable to undefine. - */ -function i18n_variable_del($name, $language) { - global $conf, $i18n_conf; - - db_query("DELETE FROM {i18n_variable} WHERE name = '%s' AND language='%s'", $name, $language); - cache_clear_all('variables:'.$language, 'cache'); - - unset($conf[$name]); - unset($i18n_conf[$name]); -} /** * Helper function to create language selector */ -function _i18n_language_select($value ='', $description ='', $weight = -20){ +function _i18n_language_select($value ='', $description ='', $weight = -20, $languages = NULL){ + $languages = $languages ? $languages : i18n_language_list(); return array( '#type' => 'select', '#title' => t('Language'), '#default_value' => $value, - '#options' => array_merge(array('' => ''), i18n_supported_languages()), + '#options' => array_merge(array('' => ''), $languages), '#description' => $description, '#weight' => $weight, ); } -function _i18n_variable_init(){ - $lang = _i18n_get_lang(); +/** + * Load language variables into array + */ +function _i18n_variable_init($language, $prefix = ''){ $variables = array(); - if ($cached = cache_get('variables:'.$lang)) { - $variables = unserialize($cached->data); + $cacheid = 'variables:'.$language.($prefix ? ':'.$prefix : ''); + if ($cached = cache_get($cacheid)) { + $variables = $cached->data; } else { - $result = db_query("SELECT * FROM {i18n_variable} WHERE language='%s'", $lang); + $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('variables:'.$lang, 'cache', serialize($variables)); + cache_set($cacheid, $variables); } - - return $variables; - + return $variables; } /** @@ -996,7 +679,7 @@ function _i18n_variable_exit(){ global $i18n_conf; global $conf; if($i18n_conf){ - $lang = _i18n_get_lang(); + $lang = i18n_get_lang(); $refresh = FALSE; // Rewritten because array_diff_assoc may fail with array variables foreach($i18n_conf as $name => $value){ @@ -1012,3 +695,160 @@ function _i18n_variable_exit(){ } } } + +/** + * 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"],"/") : ''; +} + +/** + * 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($setlanguage = NULL) { + global $language; + + return $language->language; +} + +/** + * Returns default language + */ +function i18n_default_language(){ + return language_default('language'); +} + +/** + * 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')); +} + +/** + * Get localized language list, sort alphabetically + * + * @param $all + * TRUE for all languages, not only enabled + */ +function i18n_language_list($field = 'name', $all = FALSE) { + if ($all) { + $languages = language_list(); + } + else { + $languages = language_list('enabled'); + $languages = $languages[1]; + } + $list = array(); + foreach ($languages as $language) { + $list[$language->language] = ($field == 'name') ? t($language->name) : $language->$field; + } + return $list; +} + +/** + * Get list of supported languages + * @param $all + * TRUE to get all defined languages + */ +function i18n_supported_languages($all = FALSE) { + return i18n_language_list('native', $all); +} + +/** + * @defgroup i18n_api Extended language API + * @{ + * This is an extended language API to be used by modules in i18n package. + */ + +/** + * 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". + */ diff --git a/translation/translation.info b/translation/translation.info deleted file mode 100644 index 25f136fd5c7f773032f97a91cfa17217521b9b3c..0000000000000000000000000000000000000000 --- a/translation/translation.info +++ /dev/null @@ -1,4 +0,0 @@ -name = Translation -description = Manages translations between nodes and taxonomy terms. -dependencies = i18n -package = Multilanguage - i18n diff --git a/translation/translation.module b/translation/translation.module deleted file mode 100644 index 6a8760fb4e0d54ffc1c9174dfe29f6cd7a71a933..0000000000000000000000000000000000000000 --- a/translation/translation.module +++ /dev/null @@ -1,1055 +0,0 @@ -'.t('This module is part of i18n package and provides support for translation relationships.').'

'; - $output = '

'.t('The objects you can define translation relationships for are:').'

'; - $output .= ''; - $output .= '

'. t('For more information please read the on-line help pages.', array('@i18n' =>'http://drupal.org/node/31631')) .'

'; - return $output; - break; - case 'admin/access#translation': - $output = t('

Translations

'); - $output = t('translate nodes

This one, combined with create content permissions, will allow to create node translation

'); - } - return $output; -} - -/** - * Implementation of hook_menu(). - */ -function translation_menu($may_cache) { - $items = array(); - - if ($may_cache) { - $items[] = array( - 'path' => 'admin/settings/i18n/translation', - 'title' => t('Translation'), - 'callback' => 'drupal_get_form', - 'callback arguments' => array('translation_admin_settings'), - 'access' => user_access('administer site configuration'), - 'type' => MENU_LOCAL_TASK, - ); - $items[] = array( - 'path' => 'admin/content/translation', - 'title' => t('Translations'), - 'description' => t("Manage content translations."), - 'callback' => 'translation_admin_content', - 'position' => 'left', - 'access' => user_access('translate nodes')); - $items[] = array('path' => 'admin/content/translation/overview', 'title' => t('List'), - 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10); - } - else { - if (arg(0) == 'node' && is_numeric(arg(1)) && variable_get('i18n_node_'.translation_get_node_type(arg(1)), 0)) { - $access = user_access('translate nodes'); - $type = MENU_LOCAL_TASK; - $items[] = array( - 'path' => 'node/'. arg(1) .'/translation', - 'title' => t('Translation'), - 'callback' => 'translation_node_page', - 'access' => $access, - 'type' => $type, - 'weight' => 3); - } - if(arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'taxonomy' && is_numeric(arg(3)) ) { - $items[] = array( - 'path' => 'admin/content/taxonomy/'.arg(3).'/translation', - 'title' => t('Translation'), - 'callback' => 'translation_taxonomy_admin', - 'access' => user_access('administer taxonomy'), - 'type' => MENU_LOCAL_TASK); - - } - // Special redirections and rewrite conditions - if( arg(0) == 'node' && arg(1) == 'add' && isset($_GET['translation']) && ($source_nid = $_GET['translation']) && isset($_GET['language']) && ($lang = $_GET['language']) && array_key_exists($lang, i18n_supported_languages()) ) { - // Special redirection for product types - if (arg(2) == 'product' && !arg(3) && is_numeric($source_nid) && $ptype = db_result(db_query("SELECT ptype FROM {ec_product} WHERE nid = %d", $source_nid))) { - drupal_goto(url("node/add/product/$ptype", "translation=$source_nid&language=$lang", NULL, TRUE)); - } - // Change rewrite conditions when translating node - i18n_selection_mode('translation', db_escape_string($lang)); - } - } - - return $items; -} - -/** - * Implementation of hook_perm - */ -function translation_perm(){ - return array('translate nodes'); -} - -/** - * Form builder function: settings form - */ -function translation_admin_settings(){ - $form['i18n_translation_links'] = array( - '#type' => 'radios', - '#title' => t('Language Management'), - '#default_value' => variable_get('i18n_translation_links', 0), - '#options' => array(t('Interface language depends on content.'), t('Interface language is independent')), - '#description' => t("Whether the whole page should switch language when clicking on a node translation or not."), - ); - $form['i18n_translation_node_links'] = array( - '#type' => 'radios', - '#title' => t('Links to node translations'), - '#default_value' => variable_get('i18n_translation_node_links', 0), - '#options' => array(t('None.'), t('Main page only'), t('Teaser and Main page')), - '#description' => t("Links from nodes to translated versions."), - ); - $form['i18n_translation_workflow'] = array( - '#type' => 'radios', - '#title' => t('Translation workflow'), - '#default_value' => variable_get('i18n_translation_workflow', 1), - '#options' => array(t('Disabled'), t('Enabled')), - '#description' => t("If enabled some worklow will be provided for content translation."), - ); - return system_settings_form($form); -} - -/** - * Implementation of hook_block(). - * - * This is a simple language switcher which knows nothing about translations - */ -function translation_block($op = 'list', $delta = 0) { - if ($op == 'list') { - $blocks[0]['info'] = t('Translations'); - } - elseif($op == 'view') { - $blocks['subject'] = t('Languages'); - $query = drupal_query_string_encode($_GET, array('q')); - $blocks['content'] = theme('item_list', translation_get_links($_GET['q'], empty($query) ? NULL : $query)); - } - - return $blocks; -} - -/** - * Implementation of hook_form_alter(). - */ -function translation_form_alter($form_id, &$form) { - // drupal_set_message('translation_form_alter'); - // Node edit form - if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id && variable_get('i18n_node_'.$form['type']['#value'], 0)) { - $node = $form['#node']; - $languages = i18n_supported_languages(); - // Translation workflow, default - if ($node->nid && $node->translation) { - $translations = $node->translation; - } - elseif (isset($_GET['translation']) && user_access('translate nodes')) { - // We are translating a node: node/add/type/translation/nid/lang - $translation_nid = $_GET['translation']; - $language = $_GET['language']; - // Load the node to be translated and populate fields - $trans = node_load($translation_nid); - $form['i18n']['translation_nid'] = array('#type' => 'hidden', '#value' => $translation_nid); - $form['i18n']['language']['#default_value'] = $language; - // Do not allow language change - $form['i18n']['language']['#disabled'] = TRUE; - $form['i18n']['language']['#description'] = t('Language cannot be changed while creating a translation.'); - translation_node_populate_fields($trans, $form, $node); - if($trans->trid){ - $form['i18n']['trid'] = array('#type' => 'hidden', '#value' => $trans->trid); - } - // Translations are taken from source node - $translations = $trans->translation; - $translations[$trans->language] = $trans; - } - if ($translations) { - // Unset invalid languages - foreach(array_keys($translations) as $lang) { - unset($form['i18n']['language']['#options'][$lang]); - } - // Add translation list - $form['i18n']['#title'] = t('Language and translations'); - $form['i18n']['translations'] = array( - '#type' => 'markup', - '#value' => theme('translation_node_list', $translations, FALSE) - ); - } - // Translation workflow - if (variable_get('i18n_translation_workflow', 1) && (user_access('translate nodes') || user_access('administer nodes'))) { - $form['i18n']['i18n_status'] = array( - '#type' => 'select', - '#title' => t('Translation workflow'), - '#options' => _translation_status(), - '#description' => t('Use the translation workflow to keep track of content that needs translation.')); - if($node->nid) { - $form['i18n']['i18n_status']['#default_value'] = isset($node->i18n_status) ? $node->i18n_status : TRANSLATION_STATUS_NONE; - } elseif(isset($trans)) { - $form['i18n']['i18n_status']['#default_value'] = TRANSLATION_STATUS_WORKING; - } - } else { - $form['i18n']['i18n_status'] = array('#type' => 'value', '#value' => isset($node->i18n_status) ? $node->i18n_status : TRANSLATION_STATUS_NONE); - } - // Clone files for original node ? - if (isset($trans) && is_array($trans->files) && count($trans->files)) { - $form['i18n']['translation_files'] = array( - '#type' => 'fieldset', - '#title' => t('Files from translated content'), - '#tree' => TRUE, - '#prefix' => '
', - '#suffix' => '
', - '#theme' => 'upload_form_current', - '#description' => t('You can remove the files for this translation or keep the original files and translate the description.') - ); - foreach($trans->files as $key => $file) { - $description = file_create_url((strpos($file->fid, 'upload') === false ? $file->filepath : file_create_filename($file->filename, file_create_path()))); - $description = "". check_plain($description) .""; - $form['i18n']['translation_files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => (strlen($file->description)) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => $description ); - $form['i18n']['translation_files'][$key]['size'] = array('#type' => 'markup', '#value' => format_size($file->filesize)); - $form['i18n']['translation_files'][$key]['remove'] = array('#type' => 'checkbox', '#default_value' => 0); - $form['i18n']['translation_files'][$key]['list'] = array('#type' => 'checkbox', '#default_value' => $file->list); - $form['i18n']['translation_files'][$key]['fid'] = array('#type' => 'value', '#value' => $file->fid); - } - } - // Translate taxonomy terms - // TO-DO: take care of module order. Default ordering is filename so it depends on install which is too bad!! - if(isset($trans->taxonomy) && is_array($trans->taxonomy)) { - // Set translated taxonomy terms - $form['#node']->taxonomy = array(); - foreach ($trans->taxonomy as $tid => $term) { - $translated_terms = translation_term_get_translations(array('tid' =>$tid)); - if ($translated_terms && $newterm = $translated_terms[$language]) { - $form['#node']->taxonomy[$newterm->tid] = $newterm; - //drupal_set_message("DEBUG: Translated term $tid to ". $newterm->tid); - } - // Run again taxonomy_form_alter ? - if(isset($form['taxonomy'])) { - unset($form['taxonomy']); - taxonomy_form_alter($form_id, $form); - } - } - } - } -} - -/** - * Implementation of hook_nodeapi(). - * - * Delete case is now handled in i18n_nodeapi - */ -function translation_nodeapi(&$node, $op, $arg = 0) { - if (variable_get("i18n_node_$node->type", 0)) { - switch ($op) { - case 'load': - $node->translation = translation_node_get_translations(array('nid' =>$node->nid), FALSE); - break; - case 'insert': - if($node->translation_nid) { - // If not existing translation set, update both nodes. Otherwise trid is saved by i18n module - if(!$node->trid){ - $node->trid = db_next_id('{i18n_node}_trid'); - db_query("UPDATE {i18n_node} SET trid = %d WHERE nid=%d OR nid=%d", $node->trid, $node->nid, $node->translation_nid); - } - // Clone files for node attachments - if(isset($node->translation_files)) { - foreach($node->translation_files as $fid => $file) { - if(!$file['remove']) { - // We are using revisions to have a file linked to different nodes, different descriptions - db_query("INSERT INTO {file_revisions} (fid, vid, list, description) VALUES (%d, %d, %d, '%s')", $file['fid'], $node->vid, $file['list'], $file['description']); - } - } - } - } - break; - } - } -} - -/** - * Fills up some fields from source node - */ -function translation_node_populate_fields($source, &$form, &$node, $level = 0){ - $fields = array(); - $language = $form['i18n']['language']['#default_value']; - foreach(element_children($form) as $key) { - if($key == 'nid' || $key == 'vid' || $key == 'i18n'){ - continue; - } elseif(isset($source->$key) && !isset($node->$key)) { - if($level == 0 && $key == 'parent' && is_numeric($source->parent)) { - // Translate book outline - $trans = translation_node_get_translations(array('nid' => $source->parent)); - if(isset($trans[$language])) { - $form['parent']['#default_value'] = $trans[$language]->nid; - } - } else { - $node->$key = $form[$key]['#default_value'] = $source->$key; - $fields[] = $key; - } - } elseif(!isset($form[$key]['#tree'])) { - translation_node_populate_fields($source, $form[$key], $node, $level +1); - } - } - // For debugging - //if($fields) drupal_set_message("Populated fields ($level): ". implode(', ', $fields)); -} - -/** - * Multilingual Nodes support - */ - -/** - * This is the callback for the tab 'translation' for nodes - */ -function translation_node_page() { - $args = func_get_args(); - $op = $args[0]; - $nid = arg(1); - $node = node_load($nid); - // If node has no language, just warning message. Function returns here - if(!$node->language) { - form_set_error('language', t("You need to set a language before creating a translation.")); - drupal_goto("node/$nid/edit"); - } - drupal_set_title($node->title); - $output = ''; - switch($op){ - case 'select': - $output .= translation_node_overview($node); - $output .= translation_node_select($node, $args[1]); - break; - case 'remove': - case t('Remove'): - db_query("UPDATE {i18n_node} SET trid = NULL WHERE nid=%d", $node->nid); - drupal_set_message("The node has been removed from the translation set"); - drupal_goto("node/$node->nid/translation"); - default: - $output .= translation_node_overview($node); - } - return $output; -} - -/** - * Form builder function - */ -function translation_node_form($node, $lang, $list){ - $form['node'] = array('#type' => 'value', '#value' =>$node); - $form['language'] = array('#type' => 'hidden', '#value' => $lang); - $form['source_nid'] = array('#type' => 'hidden', '#value' => $node->nid); - $form['trid'] = array('#type' => 'hidden', '#value' => $node->trid); - $languages = i18n_supported_languages(); - - $form['nodes']['nid'] = array( - '#type' => 'radios', '#title' => t('Select translation for %language', array('%language' => $languages[$lang])), - '#default_value' => isset($node->translation[$lang]) ? $node->translation[$lang]->nid : '', - '#options' => $list); - $form['pager'] = array('#value' => theme('pager')); - $form['submit'] = array('#type' => 'submit', '#value' => t('Save')); - return $form; -} - -/** - * Menu callback: administration page for node translations - */ -function translation_admin_content() { - $output = ''; - $defaults = array('translation_language' => i18n_get_lang(), 'source_status' => TRANSLATION_STATUS_SOURCE); - // Filter forms - $output .= drupal_get_form('node_filter_form'); - $output .= drupal_get_form('translation_filter_form', $defaults); - // Node listing depending on previous forms - $output .= drupal_get_form('translation_admin_nodes'); - - return $output; -} - -/** - * Form builder. Administrative form for node translations - */ -function translation_admin_nodes() { - // First, translation filter because it may need parameters for join conditions - $filter = translation_build_filter_query(); - $where = $filter['where']; - $params = $filter['args']; - $join = $filter['join']; - - $filter = node_build_filter_query(); - // Remove WHERE - if($filter['where']) { - $where += array(str_replace('WHERE', '', $filter['where'])); - } - - $params = array_merge($params, $filter['args']); - - $join .= $filter['join']; - - $sql = "SELECT n.nid, n.type, n.title, n.status, u.name, u.uid, i.language, i2.nid AS translation_nid, i2.status AS translation_status ". - "FROM {node} n INNER JOIN {users} u ON n.uid = u.uid INNER JOIN {i18n_node} i ON n.nid = i.nid ". - "LEFT JOIN {i18n_node} i2 ON i.trid = i2.trid AND 0 != i2.trid AND i.language != i2.language $join". - (count($where) ? ' WHERE '.implode(' AND ', $where) : ''); - - // Fetch data - //drupal_set_message("DEBUG:query: $sql"); - //drupal_set_message("DEBUG:params: ".implode(', ', $params)); - i18n_selection_mode('off'); - $result = pager_query(db_rewrite_sql($sql), 50, 0, NULL, $params); - i18n_selection_mode('reset'); - - $languages = i18n_supported_languages(); - $translation_status = _translation_status(); - - // Fetch language for translations - $language = isset($_SESSION['translation_filter']['translation_language']) ? $_SESSION['translation_filter']['translation_language'] : i18n_get_lang(); - $destination = drupal_get_destination(); - while ($node = db_fetch_object($result)) { - $nodes[$node->nid] = ''; - $form['language'][$node->nid] = array('#value' => $languages[$node->language]); - $form['title'][$node->nid] = array('#value' => l($node->title, 'node/'. $node->nid) .' '. theme('mark', node_mark($node->nid, $node->changed))); - $form['name'][$node->nid] = array('#value' => node_get_types('name', $node)); - $form['username'][$node->nid] = array('#value' => theme('username', $node)); - $form['status'][$node->nid] = array('#value' => ($node->status ? t('published') : t('not published'))); - if ($node->translation_nid) { - $form['translation_status'][$node->nid] = array('#value' => $translation_status[$node->translation_status]); - $form['operations'][$node->nid] = array('#value' => l(t('edit translation'), 'node/'. $node->translation_nid .'/edit', array(), $destination)); - } else { - $form['translation_status'][$node->nid] = array('#value' => '--'); - if($language == $node->language) { - $form['operations'][$node->nid] = array('#value' => '--'); - } else { - $form['operations'][$node->nid] = array('#value' => l(t('create translation'), 'node/add/'.$node->type, array(), "translation=$node->nid&language=$language"). - ' | '.l(t('select node'), "node/$node->nid/translation/select/$language", array(), $destination)); - } - } - } - /* - $form['nodes'] = array('#type' => 'checkboxes', '#options' => $nodes); - */ - $form['pager'] = array('#value' => theme('pager', NULL, 50, 0)); - return $form; -} - - -/** - * Build query for node administration filters based on session. - */ -function translation_build_filter_query() { - - // Build query - $where = $args = array(); - $join = ''; - // This will produce an empty join - if (!is_array($_SESSION['translation_filter'])) { - $_SESSION['translation_filter'] = array(); - } - foreach ($_SESSION['translation_filter'] as $type => $value) { - switch($type) { - case 'source_language': - $where[] = "i.language = '%s'"; - $args[] = $value; - break; - case 'translation_language': - $join .= " AND i2.language ='".db_escape_string($value)."' "; - break; - case 'source_status': - $where[] = "i.status = %d"; - $args[] = $value; - break; - case 'translation_status': - $join .= " AND i2.status = ".db_escape_string($value); - break; - } - } - - return array('where' => $where, 'join' => $join, 'args' => $args); -} - -/** - * Returns form for translation administration filters. - */ -function translation_filter_form($defaults = array()) { - $session = &$_SESSION['translation_filter']; - $session = is_array($session) ? $session : $defaults; - - // Save defaults for form reset - $form['_defaults'] = array('#type' => 'value', '#value' => $defaults); - $form['filters'] = array('#type' => 'fieldset', - '#title' => t('And translation conditions are'), - ); - $languages = i18n_supported_languages(); - // Translation and language conditions - - $form['filters']['source_language'] = array('#type' => 'select', '#title' => t('source language'), - '#options' => array('' => '') + $languages, '#default_value' => $session['source_language']); - $form['filters']['source_status'] = array('#type' => 'select', '#title' => t('source status'), - '#options' => array('' => '') + _translation_status(), '#default_value' => $session['source_status']); - $form['filters']['translation_language'] = array('#type' => 'select', '#title' => t('translation language'), - '#options' => array('' => '') + $languages, '#default_value' => $session['translation_language']); - $form['filters']['translation_status'] = array('#type' => 'select', '#title' => t('translation status'), - '#options' => array('' => '') + _translation_status(), '#default_value' => $session['translation_status']); - $form['filters']['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Filter')); - if (count($session)) { - $form['filters']['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset')); - } - return $form; -} - -function theme_translation_filter_form($form) { - $form['filters']['source_language']['#prefix'] = '
'; - $form['filters']['source_status']['#suffix'] = ''; - $form['filters']['buttons']['#prefix'] = ''; - $form['filters']['buttons']['#suffix'] = '
'; - return drupal_render($form); -} -/** - * Builds translation admin filters - */ -function translation_filter_form_submit($form_id, $form_values) { - $op = $_POST['op']; - $session = &$_SESSION['translation_filter']; - - switch ($op) { - case t('Filter'): - $session = array(); - foreach($form_values as $name => $value) { - if($value) $session[$name] = $value; - } - break; - case t('Reset'): - $session = $form_values['_defaults']; - break; - } - -} - -/** - * Theme node administration overview. - */ -function theme_translation_admin_nodes($form) { - // Overview table: - $header = array(t('Language'), t('Title'), t('Type'), t('Author'), t('Status'), t('Translation status'), t('Operations')); - - $output .= drupal_render($form['options']); - if (isset($form['title']) && is_array($form['title'])) { - foreach (element_children($form['title']) as $key) { - $row = array(); - $row[] = drupal_render($form['language'][$key]); - $row[] = drupal_render($form['title'][$key]); - $row[] = drupal_render($form['name'][$key]); - $row[] = drupal_render($form['username'][$key]); - $row[] = drupal_render($form['status'][$key]); - $row[] = drupal_render($form['translation_status'][$key]); - $row[] = drupal_render($form['operations'][$key]); - $rows[] = $row; - } - - } - else { - $rows[] = array(array('data' => t('No posts available.'), 'colspan' => '6')); - } - - $output .= theme('table', $header, $rows); - if ($form['pager']['#value']) { - $output .= drupal_render($form['pager']); - } - - $output .= drupal_render($form); - - return $output; -} - -/** - * Shows overview of current translations plus remove button - */ -function translation_node_overview($node) { - $languages = i18n_supported_languages(); - unset($languages[$node->language]); - // $output = t('

Current translations

'); - $header = array(t('Language'), t('Title'), t('Status'), t('Options')); - // Special links for product nodes - $createlink = $node->type == 'product' ? "node/add/$node->type/$node->ptype" : "node/add/$node->type"; - foreach($languages as $lang => $langname){ - $options = array(); - if(isset($node->translation[$lang])){ - $trnode = $node->translation[$lang]; - $title = l($trnode->title, 'node/'.$trnode->nid); - $status = $trnode->status ? t('Published') : t('Not published'); - } else { - $title = t('Not translated'); - $options[] = l(t('create translation'), $createlink, array(), "translation=$node->nid&language=$lang"); - $status = '--'; - } - $options[] = l(t('select node'), "node/$node->nid/translation/select/$lang"); - $rows[] = array($langname, $title, $status, implode(" | ", $options)); - } - $output .= theme('table', $header, $rows); - if($node->trid){ - $output .= drupal_get_form('translation_node_remove', $node); - } - return theme('box', t('Current translations'), $output); -} - -/** - * Form to remove node from translation set - */ -function translation_node_remove($node) { - $form['submit'] = array('#type' => 'submit', '#value' => t('Remove'), '#suffix' => t('Remove node from this translation set')); - $form['#action'] = url('node/'.$node->nid.'/translation/remove'); - return $form; -} - -/** - * Form to select a translation from existing nodes - */ -function translation_node_select($node, $lang) { - $languages = i18n_supported_languages(); - // Disable i18n rewrite. Order by trid to show first nodes with no translation - i18n_selection_mode('off'); - $result = pager_query(db_rewrite_sql("SELECT n.nid, n.title FROM {node} n INNER JOIN {i18n_node} i ON n.nid = i.nid WHERE i.language = '%s' ORDER BY i.trid"), 40, 0, NULL, $lang); - i18n_selection_mode('reset'); - while($trnode = db_fetch_object($result)){ - $list[$trnode->nid] = l($trnode->title, "node/$trnode->nid") ; - } - if($list){ - return drupal_get_form('translation_node_form', $node, $lang, $list); - } else { - return t("

No nodes available in %language

", array('%language' => $languages[$lang]) ); - } -} - -/** - * Process translation node form - */ -function translation_node_form_submit($form_id, $form_values){ - $op = $_POST['op']; - $source_nid = $form_values['source_nid']; - $language = $form_values['language']; - $nid = $form_values['nid']; - if( $source_nid && $language && $nid ) { - if($trid = $form_values['trid']){ - // Delete old translations - db_query("UPDATE {i18n_node} SET trid = 0 WHERE trid = %d AND language = '%s'", $trid, $language); - } else { - $trid = db_next_id('{i18n_node}_trid'); - } - db_query("UPDATE {i18n_node} SET trid = %d WHERE nid=%d OR nid=%d", $trid, $source_nid, $nid); - drupal_set_message(t('The translation has been saved')); - - } -} - -/** - * Taxonomy hook - * $edit parameter is an array, not an object !! - */ - // $op = insert, update, delete -function translation_taxonomy($op, $type = NULL, $edit = NULL) { - if (!is_array($op)) { - switch ("$type/$op") { - case 'term/insert': - case 'term/update': - if (!$edit['language'] && $edit['trid']) { - // Removed language, remove trid - db_query('UPDATE {term_data} SET trid=0 WHERE tid=%d', $edit['tid']); - if(db_affected_rows()) drupal_set_message(t('Removed translation information from term')); - } - break; - } - } else { - return $op; - } -} -/** - * Implementation of hook_link(). - */ -function translation_link($type, $node = NULL, $teaser = FALSE) { - $languages = i18n_supported_languages(); - $links = array(); - if ($type == 'node' && variable_get('i18n_translation_node_links', 0) > ($teaser ? 1 : 0) && $node->translation) { - foreach ($node->translation as $lang => $trnode) { - // Add node link if published - if($trnode->status) { - $baselang = variable_get('i18n_translation_links', 0) ? i18n_get_lang() : $lang; - $links['translation-'.$lang]= theme('translation_node_link', $trnode , $lang, $baselang); - } - } - } - return $links; -} - -/** - * Returns a list for terms for vocabulary, language - */ -function translation_vocabulary_get_terms($vid, $lang, $status = 'all') { - switch($status){ - case 'translated': - $andsql = ' AND trid > 0'; - break; - case 'untranslated': - $andsql = ' AND trid = 0'; - break; - default: - $andsql = ''; - } - $result = db_query("SELECT * FROM {term_data} WHERE vid=%d AND language='%s' $andsql", $vid, $lang); - $list = array(); - while ($term = db_fetch_object($result)) { - $list[$term->tid] = $term->name; - } - return $list; -} - -/** - * Get translations - * - * @param $params - * array of parameters - * @param $getall - * TRUE to get the also node itself - */ -function translation_node_get_translations($params, $getall = TRUE) { - foreach($params as $field => $value) { - $conds[] = "b.$field = '%s'"; - $values[] = $value; - } - if(!$getall){ // If not all, a parameter must be nid - $conds[] = "n.nid != %d"; - $values[] = $params['nid']; - } - $conds[] = "b.trid != 0"; - $sql = 'SELECT n.nid, n.title, n.status, a.language FROM {node} n INNER JOIN {i18n_node} a ON n.nid = a.nid INNER JOIN {i18n_node} b ON a.trid = b.trid WHERE '. implode(' AND ', $conds); - - i18n_selection_mode('off'); - $result = db_query(db_rewrite_sql($sql), $values); - i18n_selection_mode('reset'); - - $items = array(); - while ($node = db_fetch_object($result)) { - $items[$node->language] = $node; - } - return $items; -} - -/** - * Returns node type for nid - */ -function translation_get_node_type($nid) { - return db_result(db_query('SELECT type FROM {node} WHERE nid=%d', $nid)); -} - -/** - * Multilingual Taxonomy - * - */ - -/** - * This is the callback for taxonomy translations - * - * Gets the urls: - * admin/taxonomy/i18n/term/xx - * admin/taxonomy/i18n/term/new/xx - * admin/taxonomy/vid/translation/op/trid - */ - -function translation_taxonomy_admin() { - $vid = arg(3); - $op = $_POST['op'] ? $_POST['op'] : arg(5); - $edit = $_POST['edit']; - - switch ($op) { - case t('Save'): - case 'edit': - drupal_set_title(t('Edit term translations')); - $output = translation_taxonomy_form($vid, arg(6), $edit); - break; - case t('Submit'): - drupal_set_title(t('Submit')); - translation_taxonomy_term_save($edit); - $output = translation_taxonomy_overview($vid); - break; - case 'delete': - //print theme('page', node_delete($edit), t('Delete')); - break; - default: - $output = translation_taxonomy_overview($vid); - } - return $output; -} - -/** - * Generate a tabular listing of translations for vocabularies. - */ -function translation_taxonomy_overview($vid) { - $vocabulary = taxonomy_get_vocabulary($vid); - drupal_set_title(check_plain($vocabulary->name)); - - $languages = i18n_supported_languages(); - $header = array_merge($languages, array(t('Operations'))); - $links = array(); - $types = array(); - // Get terms/translations for this vocab - $result = db_query('SELECT * FROM {term_data} t WHERE vid=%d',$vocabulary->vid); - $terms = array(); - while ($term = db_fetch_object($result)) { - if($term->trid && $term->language) { - $terms[$term->trid][$term->language] = $term; - } - } - // Reorder data for rows and languages - foreach ($terms as $trid => $terms) { - $thisrow = array(); - foreach ($languages as $lang => $name) { - if (array_key_exists($lang, $terms)) { - $thisrow[] = $terms[$lang]->name; - } - else { - $thisrow[] = '--'; - } - } - $thisrow[] = l(t('edit'), "admin/content/taxonomy/$vid/translation/edit/$trid"); - $rows[] = $thisrow; - } - $output .= theme('table', $header, $rows); - $output .= l(t('new translation'), "admin/content/taxonomy/$vid/translation/edit/new"); - return $output; -} - -/** - * Produces a vocabulary translation form - */ -function translation_taxonomy_form($vid, $trid = NULL, $edit = array()) { - $languages = i18n_supported_languages(); - if ($trid == 'new') { - $translations = array(); - } else { - $form['trid'] = array('#type' => 'hidden', '#value' => $trid); - $translations = translation_term_get_translations(array('trid' =>$trid)); - } - //var_dump($translations); - $vocabulary = taxonomy_get_vocabulary($vid); - - // List of terms for languages - foreach ($languages as $lang => $langname) { - $current = isset($translations[$lang]) ? $translations[$lang]->tid : ''; - $list = translation_vocabulary_get_terms($vid, $lang, 'all'); - $list[''] = '--'; - $form[$lang] = array('#type' => 'fieldset', '#tree' => TRUE); - $form[$lang]['tid'] = array( - '#type' => 'select', - '#title' => $langname, - '#default_value' => $current, - '#options' => $list - ); - $form[$lang]['old'] = array('#type' => 'hidden', '#value' =>$current); - } - - $form['submit'] = array('#type' => 'submit', '#value' => t('Save')); - return drupal_get_form('translation_taxonomy', $form); -} - -/** - * Process vocabulary translation form - */ -function translation_taxonomy_submit($form_id, $form_values) { - $trid = $form_values['trid']; - $languages = i18n_supported_languages(); - $translations = array(); - // Delete old translations - if($trid){ - db_query("UPDATE {term_data} SET trid = 0 WHERE trid=%d", $trid); - } - foreach ($languages as $lang => $name) { - if (is_numeric($form_values[$lang]['tid'])) { - $translations[$lang] = $form_values[$lang]['tid']; - } - } - if(count($translations)) { - $trid = is_numeric($trid) ? $trid : db_next_id('{term_data}_trid'); - db_query('UPDATE {term_data} SET trid=%d WHERE tid IN(%s)', $trid, implode(',',$translations)); - } - drupal_set_message(t('Term translations have been updated')); -} - -/** - * Converts a list of arrays to an array of the form keyfield => namefield - */ -function translation_array2list($data, $keyfield, $namefield = 'name') { - foreach ($data as $key => $value) { - if (is_array($data)) { - $list[$value[$keyfield]] = $value[$namefield]; - } - else { - $list[$value->$keyfield] = $value->$namefield; - } - } - return $list; -} - -/** - * Get term translations - * - * @return - * An array of the from lang => Term - */ -function translation_term_get_translations($params, $getall = TRUE) { - foreach($params as $field => $value) { - $conds[] = "i.$field = '%s'"; - $values[] = $value; - } - if(!$getall){ // If not all, a parameter must be tid - $conds[] = "t.tid != %d"; - $values[] = $params['tid']; - } - $conds[] = "t.trid != 0"; - $sql = 'SELECT t.* FROM {term_data} t INNER JOIN {term_data} i ON t.trid = i.trid WHERE '. implode(' AND ', $conds);; - $result = db_query($sql, $values); - $items = array(); - while ($data = db_fetch_object($result)) { - $items[$data->language] = $data; - } - return $items; -} - -/** - * Produces url of translated page - */ -function translation_url($url, $lang) { - global $i18n_langpath; - // If !url get from original request - if (!$url) { - $url = _i18n_get_original_path(); - } - // If url has lang_prefix, remove it - i18n_get_lang_prefix($url, true); - // are we looking at a node? - if (preg_match("/^(node\/)([0-9]*)$/",$url,$matches)) { - if ($nid = translation_node_nid($matches[2], $lang)) { - $url = "node/$nid"; - } - } - // or a taxonomy term - elseif (preg_match("/^(taxonomy\/term\/)([^\/]*)$/",$url,$matches)) {//or at a taxonomy-listing? - if ($str_tids = translation_taxonomy_tids($matches[2], $lang)) { - $url = "taxonomy/term/$str_tids"; - } - } - - return $url; -} - -/** - * Returns an url-part, pointing to the translated node, if exists - */ -function translation_node_nid($nid, $lang) { - $sql = 'SELECT n.nid FROM {i18n_node} n INNER JOIN {i18n_node} a ON n.nid = a.nid INNER JOIN {i18n_node} b ON a.trid = b.trid AND b.nid =%d WHERE n.nid != %d AND n.language = \'%s\' AND a.trid != 0'; - return db_result(db_query($sql, $nid, $nid, $lang)); -} - -/** - * Returns an url for the translated taxonomy-page, if exists - */ -function translation_taxonomy_tids($str_tids, $lang) { - if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str_tids)) { - $separator = '+'; - // The '+' character in a query string may be parsed as ' '. - $tids = preg_split('/[+ ]/', $str_tids); - } - else if (preg_match('/^([0-9]+,)*[0-9]+$/', $str_tids)) { - $separator = ','; - $tids = explode(',', $str_tids); - } - else { - return; - } - $translated_tids = array(); - foreach ($tids as $tid) { - if ($translated_tid = translation_term_get_translations(array('tid' =>$tid))) { - $translated_tids[] = $translated_tid[$lang]->tid; - } - } - return implode($separator, $translated_tids); -} - -/** - * function translation_get_links - * - * Returns an array of links for all languages, with or without names - */ -function translation_get_links($path = '', $query = NULL) { - $current = i18n_get_lang(); - foreach(i18n_supported_languages() as $lang => $name){ - $url = translation_url($path, $lang); - $links[]= theme('i18n_link', $name, i18n_path($url, $lang) , $lang, $query); - } - return $links; -} - -/** - * Return status for translations workflow - */ -function _translation_status(){ - return array( - TRANSLATION_STATUS_NONE => t('None'), - TRANSLATION_STATUS_SOURCE => t('Source content (to be translated)'), - TRANSLATION_STATUS_WORKING => t('Translation in progress'), - TRANSLATION_STATUS_TRANSLATED => t('Translated content'), - TRANSLATION_STATUS_UPDATED => t('Source updated (to update translation)')); -} - -/** - * Theme a link to node translation - * - * Since Drupal 5, returns array instead of html - */ -function theme_translation_node_link($node, $lang, $baselang = NULL, $title = FALSE){ - $baselang = $baselang ? $baselang : $lang; - - if($title){ - $name = $node->title; - } else { - $languages = i18n_supported_languages(); - $name = $languages[$lang]; - } - return array( - 'title' => $name, - 'href' => i18n_path('node/'.$node->nid, $baselang) - ); -} - -/** - * Theme a link for a translation - */ -function theme_translation_link($text, $target, $lang, $separator=' ') { - return theme('i18n_link', $text, $target, $lang, $separator); -} - -/** - * Theme list of node translations - */ -function theme_translation_node_list($list){ - $header = array(t('Language'), t('Title')); - $languages = i18n_supported_languages(); - foreach($list as $lang => $node){ - $rows[] = array($languages[$lang], l($node->title, 'node/'.$node->nid) ); - } - return theme('table', $header, $rows); -}