Index: includes/bootstrap.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v retrieving revision 1.348 diff -u -p -r1.348 bootstrap.inc --- includes/bootstrap.inc 31 Jan 2010 18:31:46 -0000 1.348 +++ includes/bootstrap.inc 2 Feb 2010 10:01:04 -0000 @@ -189,12 +189,12 @@ define('LANGUAGE_NONE', 'und'); /** * The type of language used to define the content language. */ -define('LANGUAGE_TYPE_CONTENT', 'language'); +define('LANGUAGE_TYPE_CONTENT', 'language_content'); /** * The type of language used to select the user interface. */ -define('LANGUAGE_TYPE_INTERFACE', 'language_interface'); +define('LANGUAGE_TYPE_INTERFACE', 'language'); /** * The type of language used for URLs. @@ -1795,8 +1795,8 @@ function drupal_language_initialize() { */ function drupal_language_types() { return array( - LANGUAGE_TYPE_CONTENT => TRUE, LANGUAGE_TYPE_INTERFACE => TRUE, + LANGUAGE_TYPE_CONTENT => FALSE, LANGUAGE_TYPE_URL => FALSE, ); } Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.1097 diff -u -p -r1.1097 common.inc --- includes/common.inc 1 Feb 2010 07:06:14 -0000 1.1097 +++ includes/common.inc 2 Feb 2010 10:01:04 -0000 @@ -1493,12 +1493,12 @@ function fix_gpc_magic() { * The translated string. */ function t($string, array $args = array(), array $options = array()) { - global $language_interface; + global $language; static $custom_strings; // Merge in default. if (empty($options['langcode'])) { - $options['langcode'] = isset($language_interface->language) ? $language_interface->language : 'en'; + $options['langcode'] = isset($language->language) ? $language->language : 'en'; } if (empty($options['context'])) { $options['context'] = ''; Index: includes/language.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/language.inc,v retrieving revision 1.26 diff -u -p -r1.26 language.inc --- includes/language.inc 25 Jan 2010 10:38:34 -0000 1.26 +++ includes/language.inc 2 Feb 2010 10:01:04 -0000 @@ -37,17 +37,34 @@ function language_types_info() { * whose negotiation values are unchangeable and defined while defining the * language type itself. * + * @param $stored + * Optional. By default retrieves values from the 'language_types' variable to + * avoid unnecessary hook invocations. + * If set to FALSE retrieves values from the actual language type definitions. + * This allows to react to alterations performed on the definitions by modules + * installed after the 'language_types' variable is set. + * * @return * An array of language type names. */ -function language_types_configurable() { +function language_types_configurable($stored = TRUE) { $configurable = &drupal_static(__FUNCTION__); - if (!isset($configurable)) { + if ($stored && !isset($configurable)) { $types = variable_get('language_types', drupal_language_types()); $configurable = array_keys(array_filter($types)); } + if (!$stored) { + $result = array(); + foreach (language_types_info() as $type => $info) { + if (!isset($info['fixed'])) { + $result[] = $type; + } + } + return $result; + } + return $configurable; } Index: includes/locale.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/locale.inc,v retrieving revision 1.244 diff -u -p -r1.244 locale.inc --- includes/locale.inc 28 Jan 2010 07:06:57 -0000 1.244 +++ includes/locale.inc 2 Feb 2010 10:01:04 -0000 @@ -41,12 +41,12 @@ define('LOCALE_LANGUAGE_NEGOTIATION_URL_ */ /** - * Identify the language from the current content language. + * Identifies the language from the current interface language. * * @return - * The current content language code. + * The current interface language code. */ -function locale_language_from_content() { +function locale_language_from_interface() { global $language; return isset($language->language) ? $language->language : FALSE; } Index: includes/menu.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/menu.inc,v retrieving revision 1.378 diff -u -p -r1.378 menu.inc --- includes/menu.inc 30 Jan 2010 03:38:22 -0000 1.378 +++ includes/menu.inc 2 Feb 2010 10:01:04 -0000 @@ -969,7 +969,7 @@ function menu_tree_all_data($menu_name, // Use $mlid as a flag for whether the data being loaded is for the whole tree. $mlid = isset($link['mlid']) ? $link['mlid'] : 0; // Generate a cache ID (cid) specific for this $menu_name, $link, $language, and depth. - $cid = 'links:' . $menu_name . ':all-cid:' . $mlid . ':' . $GLOBALS['language_interface']->language . ':' . (int)$max_depth; + $cid = 'links:' . $menu_name . ':all-cid:' . $mlid . ':' . $GLOBALS['language']->language . ':' . (int)$max_depth; if (!isset($tree[$cid])) { // If the static variable doesn't have the data, check {cache_menu}. @@ -1081,7 +1081,7 @@ function menu_tree_page_data($menu_name, $max_depth = min($max_depth, MENU_MAX_DEPTH); } // Generate a cache ID (cid) specific for this page. - $cid = 'links:' . $menu_name . ':page-cid:' . $item['href'] . ':' . $GLOBALS['language_interface']->language . ':' . (int)$item['access'] . ':' . (int)$max_depth; + $cid = 'links:' . $menu_name . ':page-cid:' . $item['href'] . ':' . $GLOBALS['language']->language . ':' . (int)$item['access'] . ':' . (int)$max_depth; if (!isset($tree[$cid])) { // If the static variable doesn't have the data, check {cache_menu}. @@ -1229,7 +1229,7 @@ function menu_tree_page_data($menu_name, * Helper function - compute the real cache ID for menu tree data. */ function _menu_tree_cid($menu_name, $data) { - return 'links:' . $menu_name . ':tree-data:' . $GLOBALS['language_interface']->language . ':' . md5(serialize($data)); + return 'links:' . $menu_name . ':tree-data:' . $GLOBALS['language']->language . ':' . md5(serialize($data)); } /** Index: includes/path.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/path.inc,v retrieving revision 1.57 diff -u -p -r1.57 path.inc --- includes/path.inc 29 Jan 2010 22:40:41 -0000 1.57 +++ includes/path.inc 2 Feb 2010 10:01:04 -0000 @@ -44,7 +44,7 @@ function drupal_path_initialize() { * found. */ function drupal_lookup_path($action, $path = '', $path_language = NULL) { - global $language; + global $language_content; // Use the advanced drupal_static() pattern, since this is called very often. static $drupal_static_fast; if (!isset($drupal_static_fast)) { @@ -71,7 +71,7 @@ function drupal_lookup_path($action, $pa } } - $path_language = $path_language ? $path_language : $language->language; + $path_language = $path_language ? $path_language : $language_content->language; if ($action == 'wipe') { $cache = array(); Index: modules/field/field.multilingual.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.multilingual.inc,v retrieving revision 1.6 diff -u -p -r1.6 field.multilingual.inc --- modules/field/field.multilingual.inc 4 Dec 2009 16:49:46 -0000 1.6 +++ modules/field/field.multilingual.inc 2 Feb 2010 10:01:04 -0000 @@ -132,8 +132,8 @@ function field_multilingual_valid_langua if (in_array($langcode, $enabled_languages)) { return $langcode; } - global $language; - $langcode = $default ? language_default('language') : $language->language; + global $language_content; + $langcode = $default ? language_default('language') : $language_content->language; if (in_array($langcode, $enabled_languages)) { return $langcode; } Index: modules/locale/locale.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.admin.inc,v retrieving revision 1.3 diff -u -p -r1.3 locale.admin.inc --- modules/locale/locale.admin.inc 25 Jan 2010 10:38:34 -0000 1.3 +++ modules/locale/locale.admin.inc 2 Feb 2010 10:01:04 -0000 @@ -479,7 +479,7 @@ function locale_languages_configure_form $form = array( '#submit' => array('locale_languages_configure_form_submit'), '#theme' => 'locale_languages_configure_form', - '#language_types' => language_types_configurable(), + '#language_types' => language_types_configurable(FALSE), '#language_types_info' => language_types_info(), '#language_providers' => language_negotiation_info(), ); @@ -667,9 +667,10 @@ function locale_languages_configure_form if (isset($info['fixed'])) { $language_types[$type] = FALSE; $negotiation = array(); - foreach ($info['fixed'] as $id) { + foreach ($info['fixed'] as $weight => $id) { if (isset($defined_providers[$id])) { $negotiation[$id] = $defined_providers[$id]; + $negotiation[$id]['weight'] = $weight; } } language_negotiation_set($type, $negotiation); Index: modules/locale/locale.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.api.php,v retrieving revision 1.8 diff -u -p -r1.8 locale.api.php --- modules/locale/locale.api.php 6 Jan 2010 03:53:40 -0000 1.8 +++ modules/locale/locale.api.php 2 Feb 2010 10:01:04 -0000 @@ -57,6 +57,8 @@ function hook_language_switch_links_alte * the following key-value pairs: * - "name": The human-readable language type identifier. * - "description": A description of the language type. + * - "fixed": An array of language provider identifiers. Defining this key + * makes the language type non-configurable. */ function hook_language_types_info() { return array( @@ -64,6 +66,9 @@ function hook_language_types_info() { 'name' => t('Custom language'), 'description' => t('A custom language type.'), ), + 'fixed_custom_language_type' => array( + 'fixed' => array('custom_language_provider'), + ), ); } Index: modules/locale/locale.install =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.install,v retrieving revision 1.55 diff -u -p -r1.55 locale.install --- modules/locale/locale.install 9 Jan 2010 22:07:56 -0000 1.55 +++ modules/locale/locale.install 2 Feb 2010 10:01:04 -0000 @@ -70,10 +70,9 @@ function locale_update_7001() { break; } - // Save new language negotiation options: UI language is tied to content - // language as this was Drupal 6 behavior. - language_negotiation_set(LANGUAGE_TYPE_CONTENT, array_flip($negotiation)); - language_negotiation_set(LANGUAGE_TYPE_INTERFACE, array(LOCALE_LANGUAGE_NEGOTIATION_CONTENT => 0)); + // Save new language negotiation options. + language_negotiation_set(LANGUAGE_TYPE_INTERFACE, array_flip($negotiation)); + language_negotiation_set(LANGUAGE_TYPE_CONTENT, array(LOCALE_LANGUAGE_NEGOTIATION_INTERFACE => 0)); language_negotiation_set(LANGUAGE_TYPE_URL, array(LOCALE_LANGUAGE_NEGOTIATION_URL => 0)); // Unset the old language negotiation system variable. Index: modules/locale/locale.module =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v retrieving revision 1.282 diff -u -p -r1.282 locale.module --- modules/locale/locale.module 14 Jan 2010 06:43:24 -0000 1.282 +++ modules/locale/locale.module 2 Feb 2010 10:01:04 -0000 @@ -24,9 +24,9 @@ define('LOCALE_LANGUAGE_NEGOTIATION_URL' define('LOCALE_LANGUAGE_NEGOTIATION_BROWSER', 'locale-browser'); /** - * The language is determined using the current content language. + * The language is determined using the current interface language. */ -define('LOCALE_LANGUAGE_NEGOTIATION_CONTENT', 'locale-content'); +define('LOCALE_LANGUAGE_NEGOTIATION_INTERFACE', 'locale-interface'); /** * The language is set based on the user language settings. @@ -69,7 +69,7 @@ function locale_help($path, $arg) { case 'admin/config/regional/language/add': return '

' . t('Add a language to be supported by your site. If your desired language is not available in the Language name drop-down, click Custom language and provide a language code and other details manually. When providing a language code manually, be sure to enter a standardized language code, since this code may be used by browsers to determine an appropriate display language.') . '

'; case 'admin/config/regional/language/configure': - $output = '

' . t("Define how to decide which language is used to display content (text that you create and edit) and user interface text (text provided by Drupal and modules, such as field labels and help text). This decision is made by evaluating a series of detection methods for languages; the first detection method that gets a result will determine which language is used for that type of text. Define the order of evaluation of language detection methods on this page.") . '

'; + $output = '

' . t("Define how to decide which language is used to display page elements (primarily text provided by Drupal and modules, such as field labels and help text). This decision is made by evaluating a series of detection methods for languages; the first detection method that gets a result will determine which language is used for that type of text. Define the order of evaluation of language detection methods on this page.") . '

'; return $output; case 'admin/config/regional/language/configure/session': $output = '

' . t('Determine the language from a request/session parameter. Example: "http://example.com?language=de" sets language to German based on the use of "de" within the "language" parameter.') . '

'; @@ -476,12 +476,21 @@ function locale_entity_info_alter(&$enti /** * Implements hook_language_types_info(). + * + * Defines the three core language types: + * - Interface language is the only configurable language type in core. It is + * used by t() as the default language if none is specified. + * - Content language is by default non-configurable and inherits the interface + * language negotiated value. It is used by the Field API to determine the + * display language for fields if no explicit value is specified. + * - URL language is by default non-configurable and is determined through the + * URL language provider. It is used by l() as the default language if none is + * specified. */ function locale_language_types_info() { return array( LANGUAGE_TYPE_CONTENT => array( - 'name' => t('Content language detection'), - 'description' => t('Order of language detection methods for content. If a version of content is available in the detected language, it will be displayed.'), + 'fixed' => array(LOCALE_LANGUAGE_NEGOTIATION_INTERFACE), ), LANGUAGE_TYPE_INTERFACE => array( 'name' => t('User interface text language detection'), @@ -544,13 +553,13 @@ function locale_language_negotiation_inf 'description' => t("Determine the language from the browser's language settings."), ); - $providers[LOCALE_LANGUAGE_NEGOTIATION_CONTENT] = array( - 'types' => array(LANGUAGE_TYPE_INTERFACE), - 'callbacks' => array('language' => 'locale_language_from_content'), + $providers[LOCALE_LANGUAGE_NEGOTIATION_INTERFACE] = array( + 'types' => array(LANGUAGE_TYPE_CONTENT), + 'callbacks' => array('language' => 'locale_language_from_interface'), 'file' => $file, 'weight' => 8, - 'name' => t('Content'), - 'description' => t('Use the detected content language.'), + 'name' => t('Interface'), + 'description' => t('Use the detected interface language.'), ); return $providers; Index: modules/locale/locale.test =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.test,v retrieving revision 1.62 diff -u -p -r1.62 locale.test --- modules/locale/locale.test 30 Jan 2010 07:59:25 -0000 1.62 +++ modules/locale/locale.test 2 Feb 2010 10:01:04 -0000 @@ -964,11 +964,11 @@ class LocaleUninstallFunctionalTest exte /** * The default language set for the UI before uninstall. */ - protected $language_interface; + protected $language; function setUp() { parent::setUp('locale'); - $this->language_interface = 'en'; + $this->language = 'en'; } /** @@ -979,12 +979,12 @@ class LocaleUninstallFunctionalTest exte // Add a new language and optionally set it as default. require_once DRUPAL_ROOT . '/includes/locale.inc'; - locale_add_language('fr', 'French', 'Français', LANGUAGE_LTR, '', '', TRUE, $this->language_interface == 'fr'); + locale_add_language('fr', 'French', 'Français', LANGUAGE_LTR, '', '', TRUE, $this->language == 'fr'); // Check the UI language. drupal_language_initialize(); - global $language_interface; - $this->assertEqual($language_interface->language, $this->language_interface, t('Current language: %lang', array('%lang' => $language_interface->language))); + global $language; + $this->assertEqual($language->language, $this->language, t('Current language: %lang', array('%lang' => $language->language))); // Enable multilingual workflow option for articles. variable_set('language_content_type_article', 1); @@ -1030,7 +1030,7 @@ class LocaleUninstallFunctionalTest exte // Check the init language logic. drupal_language_initialize(); - $this->assertEqual($language_interface->language, 'en', t('Language after uninstall: %lang', array('%lang' => $language_interface->language))); + $this->assertEqual($language->language, 'en', t('Language after uninstall: %lang', array('%lang' => $language->language))); // Check JavaScript files deletion. $this->assertTrue($result = !file_exists($js_file), t('JavaScript file deleted: %file', array('%file' => $result ? $js_file : t('found')))); @@ -1090,7 +1090,7 @@ class LocaleUninstallFrenchFunctionalTes function setUp() { parent::setUp(); - $this->language_interface = 'fr'; + $this->language = 'fr'; } } @@ -1121,8 +1121,9 @@ class LanguageSwitchingFunctionalTest ex */ function testLanguageBlock() { // Enable the language switching block. + $language_type = LANGUAGE_TYPE_INTERFACE; $edit = array( - 'locale_language[region]' => 'sidebar_first', + "locale_{$language_type}[region]" => 'sidebar_first', ); $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); @@ -1135,14 +1136,14 @@ class LanguageSwitchingFunctionalTest ex // Set language negotiation. drupal_load('module', 'locale'); include_once DRUPAL_ROOT . '/includes/language.inc'; - language_negotiation_set(LANGUAGE_TYPE_CONTENT, locale_language_negotiation_info()); + language_negotiation_set($language_type, locale_language_negotiation_info()); // Assert that the language switching block is displayed on the frontpage. $this->drupalGet(''); $this->assertText(t('Languages'), t('Language switcher block found.')); // Assert that only the current language is marked as active. - list($language_switcher) = $this->xpath('//div[@id="block-locale-language"]/div[@class="content"]'); + list($language_switcher) = $this->xpath("//div[@id=\"block-locale-{$language_type}\"]/div[@class=\"content\"]"); $links = array( 'active' => array(), 'inactive' => array(), @@ -1814,9 +1815,9 @@ class LocalizeDateFormatsFunctionalTest $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); // Set language negotiation. + $language_type = LANGUAGE_TYPE_INTERFACE; $edit = array( - 'language[enabled][locale-url]' => TRUE, - 'language_interface[enabled][locale-url]' => TRUE, + "{$language_type}[enabled][locale-url]" => TRUE, ); $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.1225 diff -u -p -r1.1225 node.module --- modules/node/node.module 30 Jan 2010 02:52:08 -0000 1.1225 +++ modules/node/node.module 2 Feb 2010 10:03:49 -0000 @@ -1266,52 +1266,6 @@ function node_build_content($node, $view } /** - * Implements hook_language_negotiation_info(). - */ -function node_language_negotiation_info() { - $providers = array(); - - $providers['node-language'] = array( - 'types' => array(LANGUAGE_TYPE_CONTENT), - 'callbacks' => array('language' => 'node_language_provider'), - 'file' => drupal_get_path('module', 'node') . '/node.module', - 'name' => t('Content'), - 'description' => t('Use the language of the displayed content.'), - ); - - return $providers; -} - -/** - * Return the language of the current node. - * - * @param $languages - * An array of valid language objects. - * - * @return - * A valid language code on success, FALSE otherwise. - */ -function node_language_provider($languages) { - require_once DRUPAL_ROOT . '/' . variable_get('path_inc', 'includes/path.inc'); - - $path = isset($_GET['q']) ? $_GET['q'] : ''; - list($language, $path) = language_url_split_prefix($path, $languages); - $language = $language ? $language : language_default(); - $path = drupal_get_normal_path($path, $language->language); - - // We cannot use args now. - $path = explode('/', $path); - // Act only if we are in a node page. - if (isset($path[0]) && isset($path[1]) && $path[0] == 'node' && $nid = intval($path[1])) { - // We cannot perform a node load here. - $result = db_query('SELECT n.language FROM {node} n WHERE n.nid = :nid', array(':nid' => $nid))->fetchAssoc(); - return $result['language']; - } - - return FALSE; -} - -/** * Generate an array which displays a node detail page. * * @param $node