Index: includes/bootstrap.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v retrieving revision 1.302 diff -u -p -r1.302 bootstrap.inc --- includes/bootstrap.inc 24 Aug 2009 00:14:18 -0000 1.302 +++ includes/bootstrap.inc 31 Aug 2009 02:23:57 -0000 @@ -161,26 +161,57 @@ define('DRUPAL_KILOBYTE', 1024); /** * No language negotiation. The default language is used. */ -define('LANGUAGE_NEGOTIATION_NONE', 0); +define('LANGUAGE_NEGOTIATION_DEFAULT', 0); /** - * Path based negotiation with fallback to default language - * if no defined path prefix identified. + * The language is determined using a URL language indicator: + * path prefix or domain according to the configuration. */ -define('LANGUAGE_NEGOTIATION_PATH_DEFAULT', 1); +define('LANGUAGE_NEGOTIATION_URL', 1); /** - * Path based negotiation with fallback to user preferences - * and browser language detection if no defined path prefix - * identified. + * The language is set basing on the user preferences. Request + * parameters are checked, then session parameters, and eventually + * the user language settings. */ -define('LANGUAGE_NEGOTIATION_PATH', 2); +define('LANGUAGE_NEGOTIATION_USER', 2); /** - * Domain based negotiation with fallback to default language - * if no language identified by domain. + * The language is set basing on the browser language settings. */ -define('LANGUAGE_NEGOTIATION_DOMAIN', 3); +define('LANGUAGE_NEGOTIATION_BROWSER', 3); + +/** + * The language is determined using the current content language. + */ +define('LANGUAGE_NEGOTIATION_CONTENT', 4); + +/** + * Content language negotiation: use the path prefix as URL language + * indicator. + */ +define('LANGUAGE_NEGOTIATION_URL_PREFIX', 0); + +/** + * Content language negotiation: use the domain as URL language + * indicator. + */ +define('LANGUAGE_NEGOTIATION_URL_DOMAIN', 1); + +/** + * The type of language used to select the user interface. + */ +define('LANGUAGE_TYPE_INTERFACE', 0x1); + +/** + * The type of language used to define the content language. + */ +define('LANGUAGE_TYPE_CONTENT', 0x2); + +/** + * Any type of language. + */ +define('LANGUAGE_TYPE_ANY', LANGUAGE_TYPE_INTERFACE | LANGUAGE_TYPE_CONTENT); /** * Language written left to right. Possible value of $language->direction. @@ -1641,16 +1672,17 @@ function get_t() { * Choose a language for the current page, based on site and user preferences. */ function drupal_language_initialize() { - global $language, $user; + global $language, $language_interface, $user; // Ensure the language is correctly returned, even without multilanguage support. // Useful for eg. XML/HTML 'lang' attributes. if (variable_get('language_count', 1) == 1) { - $language = language_default(); + $language = $language_interface = language_default(); } else { include_once DRUPAL_ROOT . '/includes/language.inc'; - $language = language_initialize(); + $language = language_initialize(LANGUAGE_TYPE_CONTENT); + $language_interface = language_initialize(LANGUAGE_TYPE_INTERFACE); } } Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.977 diff -u -p -r1.977 common.inc --- includes/common.inc 26 Aug 2009 15:00:17 -0000 1.977 +++ includes/common.inc 31 Aug 2009 02:23:57 -0000 @@ -295,6 +295,25 @@ function drupal_query_string_encode($que } /** + * Split an urlencoded query string into an array. + * + * @param $query + * The query string to split. + * @return + * An array of url decoded couples $param_name => $value. + */ +function drupal_query_string_decode($query) { + $result = array(); + if (!empty($query)) { + foreach (explode('&', $query) as $param) { + $param = explode('=', $param); + $result[$param[0]] = isset($param[1]) ? rawurldecode($param[1]) : ''; + } + } + return $result; +} + +/** * Prepare a destination query string for use in combination with drupal_goto(). * * Used to direct the user back to the referring page after completing a form. @@ -1193,12 +1212,12 @@ function fix_gpc_magic() { * The translated string. */ function t($string, array $args = array(), array $options = array()) { - global $language; + global $language_interface; static $custom_strings; // Merge in default. if (empty($options['langcode'])) { - $options['langcode'] = isset($language->language) ? $language->language : 'en'; + $options['langcode'] = isset($language_interface->language) ? $language_interface->language : 'en'; } if (empty($options['context'])) { $options['context'] = ''; Index: includes/language.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/language.inc,v retrieving revision 1.19 diff -u -p -r1.19 language.inc --- includes/language.inc 1 Feb 2009 16:45:53 -0000 1.19 +++ includes/language.inc 31 Aug 2009 02:23:57 -0000 @@ -7,69 +7,131 @@ */ /** - * Choose a language for the page, based on language negotiation settings. + * Check if a language provider is enabled. If no id is passed the first + * enabled language provider is returned. + * + * @param $type + * The language negotiation type. + * @param $provider_id + * The language provider id. + * @return + * The provider id if it is enabled, FALSE otherwise. */ -function language_initialize() { - global $user; +function language_get_negotiation($type, $provider_id = NULL) { + $negotiation = variable_get("language_negotiation_$type"); + if (empty($negotiation)) { + return LANGUAGE_NEGOTIATION_DEFAULT; + } + foreach ($negotiation as $id => $provider) { + if (empty($provider_id) || $id == $provider_id) { + return $id; + } + } + return FALSE; +} - // Configured presentation language mode. - $mode = variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE); - // Get a list of enabled languages. - $languages = language_list('enabled'); - $languages = $languages[1]; - - switch ($mode) { - case LANGUAGE_NEGOTIATION_NONE: - return language_default(); +/** + * Save a list of list of language providers. + * + * @param $type + * The language type. + * @param $provider_list + * An array of language provider ids. + */ +function language_set_negotiation($type, $provider_list) { + $negotiation = array(); + $enabled_providers = array(); + $providers_weight = array(); + $weight = 0; + + foreach (language_negotiation_info() as $id => $provider) { + $enabled_providers[$id] = FALSE; + $providers_weight[$id] = $weight++; + // Check if the provider is defined and has the right type. + if (in_array($id, $provider_list) && (!isset($provider['type']) || $provider['type'] & $type)) { + $negotiation[] = $provider; + $enabled_providers[$id] = TRUE; + } + } - case LANGUAGE_NEGOTIATION_DOMAIN: - foreach ($languages as $language) { - $host = parse_url($language->domain, PHP_URL_HOST); - if ($host && ($_SERVER['HTTP_HOST'] == $host)) { - return $language; - } - } - return language_default(); + variable_set("language_negotiation_$type", $negotiation); + variable_set("language_providers_enabled_$type", $enabled_providers); + variable_set("language_providers_weight_$type", $providers_weight); +} - case LANGUAGE_NEGOTIATION_PATH_DEFAULT: - case LANGUAGE_NEGOTIATION_PATH: - // $_GET['q'] might not be available at this time, because - // path initialization runs after the language bootstrap phase. - $args = isset($_GET['q']) ? explode('/', $_GET['q']) : array(); - $prefix = array_shift($args); - // Search prefix within enabled languages. - foreach ($languages as $language) { - if (!empty($language->prefix) && $language->prefix == $prefix) { - // Rebuild $GET['q'] with the language removed. - $_GET['q'] = implode('/', $args); - return $language; - } - } - if ($mode == LANGUAGE_NEGOTIATION_PATH_DEFAULT) { - // If we did not found the language by prefix, choose the default. - return language_default(); - } - break; - } +/** + * Return all the defined language providers. + */ +function language_negotiation_info() { + $language_providers = &drupal_static(__FUNCTION__); - // User language. - if ($user->uid && isset($languages[$user->language])) { - return $languages[$user->language]; + if (!isset($language_providers)) { + $language_providers = array(); + foreach (module_implements('language_negotiation_info') as $module) { + $language_providers += module_invoke($module, 'language_negotiation_info'); + } + // Let other modules alter the list of language providers. + drupal_alter('language_negotiation_info', $language_providers); } - // Browser accept-language parsing. - if ($language = language_from_browser()) { - return $language; + return $language_providers; +} + +/** + * Helper function used to cache the language providers results. + */ +function language_provider_invoke($provider_id, $provider = NULL) { + $results = &drupal_static(__FUNCTION__); + + if (!isset($result[$provider_id])) { + global $user; + $languages = language_list('enabled'); + $languages = $languages[1]; + + if (!isset($provider)) { + $providers = language_negotiation_info(); + $provider = $providers[$provider_id]; + } + + if (isset($provider['file'])) { + require_once DRUPAL_ROOT .'/'. $provider['file']; + } + + // If the language provider has no cache preference or this is satisified + // we can execute the callback. + $cache = !isset($provider['cache']) || $user->uid || $provider['cache'] == variable_get('cache', CACHE_DISABLED); + $callback = $provider['callback']; + $langcode = $cache && function_exists($callback) ? $callback($languages) : FALSE; + $results[$provider_id] = isset($languages[$langcode]) ? $languages[$langcode] : FALSE; } - // Fall back on the default if everything else fails. + return $results[$provider_id]; +} + +/** + * Choose a language for the give type, based on the language negotiation settings. + */ +function language_initialize($type) { + // Execute the language providers in the order they were set up and return the first + // valid language found. + $negotiation = variable_get("language_negotiation_$type", array()); + foreach ($negotiation as $id => $provider) { + $language = language_provider_invoke($id, $provider); + if ($language) { + return $language; + } + } + // If no other language was found use the default one. return language_default(); } /** * Identify language from the Accept-language HTTP header we got. + * + * We perform browser accept-language parsing only if page cache is disabled, + * otherwise we would cache an user-specific preference. */ -function language_from_browser() { +function language_from_browser($languages) { // Specified by the user via the browser's Accept Language setting // Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5" $browser_langs = array(); @@ -90,12 +152,89 @@ function language_from_browser() { arsort($browser_langs); // Try to find the first preferred language we have - $languages = language_list('enabled'); foreach ($browser_langs as $langcode => $q) { - if (isset($languages['1'][$langcode])) { - return $languages['1'][$langcode]; + if (isset($languages[$langcode])) { + return $langcode; } } + + return FALSE; +} + +/** + * Return the current content language code. + */ +function language_from_content() { + global $language; + return $language->language; +} + +/** + * Identify language from the user preferences. + */ +function language_from_user($languages) { + // Request parameter. + if (isset($_GET['language_ui']) && isset($languages[$langcode = $_GET['language_ui']])) { + return $_SESSION['language_ui'] = $langcode; + } + // Session parameter. + if (isset($_SESSION['language_ui'])) { + return $_SESSION['language_ui']; + } + // User preference (only for logged users). + global $user; + if ($user->uid) { + return $user->language; + } + // No language preference from the user. + return FALSE; +} + +/** + * Identify language via URL prefix or domain. + */ +function language_from_url($languages) { + $url_language = FALSE; + + switch (variable_get('language_negotiation_url', LANGUAGE_NEGOTIATION_URL_PREFIX)) { + case LANGUAGE_NEGOTIATION_URL_PREFIX: + // $_GET['q'] might not be available at this time, because + // path initialization runs after the language bootstrap phase. + list($language, $_GET['q']) = language_split_prefix(isset($_GET['q']) ? $_GET['q'] : NULL, $languages); + if ($language !== FALSE) { + $url_language = $language->language; + } + break; + + case LANGUAGE_NEGOTIATION_URL_DOMAIN: + foreach ($languages as $language) { + $host = parse_url($language->domain, PHP_URL_HOST); + if ($host && ($_SERVER['HTTP_HOST'] == $host)) { + $url_language = $language->language; + break; + } + } + break; + } + + return $url_language; +} + +/** + * Parse the given path and return the language identified by the + * prefix and the actual path. + */ +function language_split_prefix($path, $languages) { + $args = empty($path) ? array() : explode('/', $path); + $prefix = array_shift($args); + // Search prefix within enabled languages. + foreach ($languages as $language) { + if (!empty($language->prefix) && $language->prefix == $prefix) { + // Rebuild $path with the language removed. + return array($language, implode('/', $args)); + } + } + return array(FALSE, $path); } /** @@ -103,7 +242,7 @@ function language_from_browser() { * as those of the url() function. */ function language_url_rewrite(&$path, &$options) { - global $language; + global $language, $user; // Only modify relative (insite) URLs. if (!$options['external']) { @@ -113,32 +252,37 @@ function language_url_rewrite(&$path, &$ $options['language'] = $language; } - switch (variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE)) { - case LANGUAGE_NEGOTIATION_NONE: - // No language dependent path allowed in this mode. - unset($options['language']); - break; - - case LANGUAGE_NEGOTIATION_DOMAIN: - if ($options['language']->domain) { - // Ask for an absolute URL with our modified base_url. - $options['absolute'] = TRUE; - $options['base_url'] = $options['language']->domain; - } - break; + // If the user is anonymous and the user language provider is enabled as for the + // UI language negotiation, we must preserve any explicit user language preference + // even with cookie disabled. + // We don't make any check on the language validity here, as it would be too + // much expensive in a page with many links. + if (!$user->uid && isset($_GET['language_interface']) && variable_get('language_negotiation_user_request_param', FALSE) && + language_get_negotiation(LANGUAGE_TYPE_INTERFACE, LANGUAGE_NEGOTIATION_USER)) { + if (is_string($options['query'])) { + $options['query'] = drupal_query_string_decode($options['query']); + } + if (!isset($options['query']['language_interface'])) { + $options['query']['language_interface'] = check_plain($_GET['language_interface']); + } + } - case LANGUAGE_NEGOTIATION_PATH_DEFAULT: - $default = language_default(); - if ($options['language']->language == $default->language) { + if (language_get_negotiation(LANGUAGE_TYPE_CONTENT, LANGUAGE_NEGOTIATION_URL)) { + switch (variable_get('language_negotiation_url', LANGUAGE_NEGOTIATION_URL_PREFIX)) { + case LANGUAGE_NEGOTIATION_URL_DOMAIN: + if ($options['language']->domain) { + // Ask for an absolute URL with our modified base_url. + $options['absolute'] = TRUE; + $options['base_url'] = $options['language']->domain; + } break; - } - // Intentionally no break here. - case LANGUAGE_NEGOTIATION_PATH: - if (!empty($options['language']->prefix)) { - $options['prefix'] = $options['language']->prefix . '/'; - } - break; + case LANGUAGE_NEGOTIATION_URL_PREFIX: + if (!empty($options['language']->prefix)) { + $options['prefix'] = $options['language']->prefix . '/'; + } + break; + } } } } Index: includes/locale.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/locale.inc,v retrieving revision 1.226 diff -u -p -r1.226 locale.inc --- includes/locale.inc 22 Aug 2009 14:34:17 -0000 1.226 +++ includes/locale.inc 31 Aug 2009 02:23:57 -0000 @@ -476,33 +476,211 @@ function locale_languages_delete_form_su * Setting for language negotiation options */ function locale_languages_configure_form() { - $form['language_negotiation'] = array( - '#title' => t('Language negotiation'), - '#type' => 'radios', - '#options' => array( - LANGUAGE_NEGOTIATION_NONE => t('None.'), - LANGUAGE_NEGOTIATION_PATH_DEFAULT => t('Path prefix only.'), - LANGUAGE_NEGOTIATION_PATH => t('Path prefix with language fallback.'), - LANGUAGE_NEGOTIATION_DOMAIN => t('Domain name only.')), - '#default_value' => variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE), - '#description' => t("Select the mechanism used to determine your site's presentation language. Modifying this setting may break all incoming URLs and should be used with caution in a production environment.") - ); + $form = array(); + + $title = t('Content language negotiation'); + // @todo We need an help text here. + $description = ''; + $table_form = _locale_languages_configure_form_language_table(LANGUAGE_TYPE_CONTENT, $title, $description); + $form[LANGUAGE_TYPE_CONTENT] = $table_form; + + $title = t('Interface language negotiation'); + // @todo We need an help text here. + $description = ''; + $table_form = _locale_languages_configure_form_language_table(LANGUAGE_TYPE_INTERFACE, $title, $description); + $form[LANGUAGE_TYPE_INTERFACE] = $table_form; + $form['submit'] = array( '#type' => 'submit', '#value' => t('Save settings') ); + + $form['#theme'] = 'locale_languages_configure_form'; + return $form; } /** + * Helper function to build a language provider table. + */ +function _locale_languages_configure_form_language_table($type, $title, $description) { + require_once DRUPAL_ROOT . '/includes/language.inc'; + + $table_form = array('#tree' => TRUE); + $table_form['weight'] = array('#tree' => TRUE); + $table_form['enabled'] = array('#tree' => TRUE); + $table_form['#language_providers'] = array(); + $table_form['#title'] = $title; + $table_form['#description'] = $description; + $table_form['#show_operations'] = FALSE; + + $language_providers = language_negotiation_info(); + $enabled_providers = variable_get("language_providers_enabled_$type", array()); + $providers_weight = variable_get("language_providers_weight_$type", array()); + $ordered_list = $providers_weight + $language_providers; + + foreach ($ordered_list as $id => $value) { + $provider = $language_providers[$id]; + if (!isset($provider['type']) || $provider['type'] & $type) { + $table_form['#language_providers'][$id] = $provider; + $table_form['weight'][$id] = array( + '#type' => 'weight', + '#default_value' => @$providers_weight[$id], + '#attributes' => array('class' => array("language-provider-weight-$type")), + ); + $table_form['title'][$id] = array('#markup' => check_plain($provider['title'])); + $table_form['enabled'][$id] = array('#type' => 'checkbox', '#default_value' => @$enabled_providers[$id]); + if ($id === LANGUAGE_NEGOTIATION_DEFAULT) { + $table_form['enabled'][$id]['#default_value'] = TRUE; + $table_form['enabled'][$id]['#attributes'] = array('disabled' => 'disabled'); + } + $table_form['description'][$id] = array('#markup' => check_markup($provider['description'])); + $config_op = ''; + if (isset($provider['config_path'])) { + $config_op = l(t('Configure'), $provider['config_path']); + // If there is at least one operation enabled show the operation column. + $table_form['#show_operations'] = TRUE; + } + $table_form['operation'][$id] = array('#markup' => $config_op); + } + } + + return $table_form; +} + +/** + * Theme the language configure form. + * + * @ingroup themeable + */ +function theme_locale_languages_configure_form($form) { + $output = ''; + + foreach (array(LANGUAGE_TYPE_CONTENT, LANGUAGE_TYPE_INTERFACE) as $type) { + $rows = array(); + $output .= ''; + $output .= '
language_ui
request parameter. If the user is logged this choice will be remembered through session, otherwise the request parameter will be sticky.')
+ );
+
+ $form['#redirect'] = 'admin/config/regional/language/configure';
+
+ return system_settings_form($form);
+}
+
/**
* @} End of "locale-languages-negotiation"
*/
Index: modules/locale/locale.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.install,v
retrieving revision 1.47
diff -u -p -r1.47 locale.install
--- modules/locale/locale.install 17 Aug 2009 19:14:40 -0000 1.47
+++ modules/locale/locale.install 31 Aug 2009 02:23:57 -0000
@@ -46,6 +46,42 @@ function locale_update_7000() {
}
/**
+ * Upgrade language negotiation settings.
+ */
+function locale_update_7001() {
+ require_once DRUPAL_ROOT . '/includes/language.inc';
+
+ switch (variable_get('language_negotiation', 0)) {
+ // LANGUAGE_NEGOTIATION_NONE
+ case 0:
+ $negotiation = array();
+ break;
+
+ // LANGUAGE_NEGOTIATION_PATH_DEFAULT
+ case 1:
+ $negotiation = array(LANGUAGE_NEGOTIATION_URL);
+ break;
+
+ // LANGUAGE_NEGOTIATION_PATH
+ case 2:
+ $negotiation = array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_USER, LANGUAGE_NEGOTIATION_BROWSER);
+ break;
+
+ // LANGUAGE_NEGOTIATION_DOMAIN
+ case 3:
+ variable_set('language_negotiation_url', LANGUAGE_NEGOTIATION_URL_DOMAIN);
+ $negotiation = array(LANGUAGE_NEGOTIATION_URL);
+ break;
+ }
+
+ language_set_negotiation(LANGUAGE_TYPE_CONTENT, $negotiation);
+ language_set_negotiation(LANGUAGE_TYPE_INTERFACE, array(LANGUAGE_NEGOTIATION_CONTENT));
+ variable_del('language_negotiation');
+
+ return array();
+}
+
+/**
* @} End of "defgroup updates-6.x-to-7.x"
*/
@@ -67,12 +103,19 @@ function locale_uninstall() {
// Clear variables.
variable_del('language_default');
variable_del('language_count');
- variable_del('language_negotiation');
- variable_del('javascript_parsed');
+ variable_del('language_negotiation_url');
+ variable_del('language_negotiation_user_request_param');
variable_del('language_content_type_default');
variable_del('language_content_type_negotiation');
variable_del('locale_cache_strings');
variable_del('locale_js_directory');
+ variable_del('javascript_parsed');
+
+ foreach (array(LANGUAGE_TYPE_CONTENT, LANGUAGE_TYPE_INTERFACE) as $type) {
+ variable_del("language_negotiation_$type");
+ variable_del("language_providers_enabled_$type");
+ variable_del("language_providers_weight_$type");
+ }
foreach (node_type_get_types() as $type => $content_type) {
$setting = variable_del('language_content_type_' . $type);
Index: modules/locale/locale.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v
retrieving revision 1.257
diff -u -p -r1.257 locale.module
--- modules/locale/locale.module 29 Aug 2009 05:46:03 -0000 1.257
+++ modules/locale/locale.module 31 Aug 2009 02:23:57 -0000
@@ -37,14 +37,6 @@ function locale_help($path, $arg) {
return $output;
case 'admin/config/regional/language/add':
return '' . t('Add all languages 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("Language negotiation settings determine the site's presentation language. Available options include:") . '
'; - $output .= '' . t('The path prefix or domain name for a language may be set by editing the available languages. In the absence of an appropriate match, the site is displayed in the default language.', array('@languages' => url('admin/config/regional/language'))) . '
'; - return $output; case 'admin/config/regional/translate': $output = '' . t('This page provides an overview of available translatable strings. Drupal displays translatable strings in text groups; modules may define additional text groups containing other translatable strings. Because text groups provide a method of grouping related strings, they are often used to focus translation efforts on specific areas of the Drupal interface.') . '
'; $output .= '' . t('Review the languages page for more information on adding support for additional languages.', array('@languages' => url('admin/config/regional/language'))) . '
'; @@ -103,6 +95,22 @@ function locale_menu() { 'file path' => 'includes', 'type' => MENU_LOCAL_TASK, ); + $items['admin/config/regional/language/configure/url'] = array( + 'title' => 'URL language provider configuration', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('locale_language_providers_url_form'), + 'access arguments' => array('administer languages'), + 'file' => 'locale.inc', + 'file path' => 'includes', + ); + $items['admin/config/regional/language/configure/user'] = array( + 'title' => 'User language provider configuration', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('locale_language_providers_user_form'), + 'access arguments' => array('administer languages'), + 'file' => 'locale.inc', + 'file path' => 'includes', + ); $items['admin/config/regional/language/edit/%'] = array( 'title' => 'Edit language', 'page callback' => 'drupal_get_form', @@ -265,13 +273,13 @@ function locale_language_selector_form($ ); // Get language negotiation settings. - $mode = variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE); + $mode = language_get_negotiation(LANGUAGE_TYPE_INTERFACE) != LANGUAGE_NEGOTIATION_DEFAULT; $form['locale']['language'] = array( '#type' => (count($names) <= 5 ? 'radios' : 'select'), '#title' => t('Language'), '#default_value' => $user_preferred_language->language, '#options' => $names, - '#description' => ($mode == LANGUAGE_NEGOTIATION_PATH) ? t("This account's default language for e-mails, and preferred language for site presentation.") : t("This account's default language for e-mails."), + '#description' => $mode ? t("This account's default language for e-mails, and preferred language for site presentation.") : t("This account's default language for e-mails."), ); return $form; } @@ -337,12 +345,61 @@ function locale_theme() { 'locale_languages_overview_form' => array( 'arguments' => array('form' => array()), ), + 'locale_languages_configure_form' => array( + 'arguments' => array('form' => array()), + ), 'locale_translation_filters' => array( 'arguments' => array('form' => array()), ), ); } +/** + * Implement hook_language_negotiation_info(): + */ +function locale_language_negotiation_info() { + $providers = array(); + + $providers[LANGUAGE_NEGOTIATION_URL] = array( + 'title' => t('URL'), + 'description' => t('The language is determined from the URL (Path prefix or domain).'), + 'callback' => 'language_from_url', + 'config_path' => 'admin/config/regional/language/configure/url', + ); + + $providers[LANGUAGE_NEGOTIATION_CONTENT] = array( + 'title' => t('Content'), + 'description' => t('The interface language is the same the negotiated content language.'), + 'callback' => 'language_from_content', + 'type' => LANGUAGE_TYPE_INTERFACE, + ); + + $providers[LANGUAGE_NEGOTIATION_USER] = array( + 'title' => t('User'), + 'description' => t('The language is determined from the language preference set in the user account.'), + 'callback' => 'language_from_user', + 'config_path' => 'admin/config/regional/language/configure/user', + 'type' => LANGUAGE_TYPE_INTERFACE, + ); + + $providers[LANGUAGE_NEGOTIATION_BROWSER] = array( + 'title' => t('Browser'), + 'description' => t('The language is determined from the browser\'s language settings.'), + 'callback' => 'language_from_browser', + 'type' => LANGUAGE_TYPE_INTERFACE, + 'cache' => CACHE_DISABLED, + ); + + $language = language_default(); + $providers[LANGUAGE_NEGOTIATION_DEFAULT] = array( + 'title' => t('Default'), + 'description' => t('The default site language (@language_name) is used.', array('@language_name' => $language->native)), + 'callback' => '', + ); + + return $providers; +} + // --------------------------------------------------------------------------------- // Locale core functionality @@ -633,8 +690,10 @@ function locale_css_alter(&$css) { */ function locale_block_info() { $block['language-switcher']['info'] = t('Language switcher'); + $block['language-ui-switcher']['info'] = t('Interface language switcher'); // Not worth caching. $block['language-switcher']['cache'] = BLOCK_NO_CACHE; + $block['language-ui-switcher']['cache'] = BLOCK_NO_CACHE; return $block; } @@ -646,10 +705,23 @@ function locale_block_info() { * web addresses, so we can actually link to other language versions. */ function locale_block_view($delta = '') { - if (variable_get('language_count', 1) > 1 && variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) != LANGUAGE_NEGOTIATION_NONE) { + switch ($delta) { + case 'language-switcher': + return _locale_language_switcher(); + case 'language-ui-switcher': + return _locale_language_ui_switcher(); + } +} + +/** + * Return the language switcher block. + */ +function _locale_language_switcher() { + if (variable_get('language_count', 1) > 1 && language_get_negotiation(LANGUAGE_TYPE_CONTENT) != LANGUAGE_NEGOTIATION_DEFAULT) { $path = drupal_is_front_page() ? '