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 28 Aug 2009 00:24:03 -0000 @@ -159,28 +159,39 @@ define('DRUPAL_AUTHENTICATED_RID', 2); define('DRUPAL_KILOBYTE', 1024); /** - * No language negotiation. The default language is used. + * No UI 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 UI language is the same as the content language if an URL + * language indicator is found. */ -define('LANGUAGE_NEGOTIATION_PATH_DEFAULT', 1); +define('LANGUAGE_NEGOTIATION_CONTENT', 1); /** - * Path based negotiation with fallback to user preferences - * and browser language detection if no defined path prefix - * identified. + * The UI 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 UI language is set basing on the browser language settings. */ -define('LANGUAGE_NEGOTIATION_DOMAIN', 3); +define('LANGUAGE_NEGOTIATION_BROWSER', 3); + +/** + * 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); /** * Language written left to right. Possible value of $language->direction. @@ -1641,16 +1652,22 @@ 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_ui, $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_ui = language_default(); } else { include_once DRUPAL_ROOT . '/includes/language.inc'; - $language = language_initialize(); + // Content language should be explicitly identified by its URL, if we cannot find + // a proper language indicator we assume the content has the default language. + $language = language_from_url(); + if (!$language) { + $language = language_default(); + } + $language_ui = language_ui_initialize(); } } 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 27 Aug 2009 22:22:39 -0000 @@ -1193,12 +1193,12 @@ function fix_gpc_magic() { * The translated string. */ function t($string, array $args = array(), array $options = array()) { - global $language; + global $language_ui; static $custom_strings; // Merge in default. if (empty($options['langcode'])) { - $options['langcode'] = isset($language->language) ? $language->language : 'en'; + $options['langcode'] = isset($language_ui->language) ? $language_ui->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 28 Aug 2009 00:27:17 -0000 @@ -7,95 +7,177 @@ */ /** - * Choose a language for the page, based on language negotiation settings. + * Return the current main language negotiation mode. */ -function language_initialize() { - global $user; +function language_negotiation() { + $negotiation = variable_get('language_negotiation'); + if (empty($negotiation)) { + return LANGUAGE_NEGOTIATION_DEFAULT; + } + return key($negotiation); +} + +/** + * Return all the defined language providers. + */ +function language_providers() { + $language_providers = &drupal_static(__FUNCTION__); + if (!isset($language_providers)) { + $language_providers = array(); + foreach (module_implements('language_providers') as $module) { + $language_providers += module_invoke($module, 'language_providers'); + } + // Let other modules alter the list of language providers. + drupal_alter('language_providers', $language_providers); + } + return $language_providers; +} - // Configured presentation language mode. - $mode = variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE); - // Get a list of enabled languages. +/** + * Choose a language for the interface, based on the language negotiation settings. + */ +function language_ui_initialize() { $languages = language_list('enabled'); $languages = $languages[1]; - switch ($mode) { - case LANGUAGE_NEGOTIATION_NONE: - return language_default(); - - case LANGUAGE_NEGOTIATION_DOMAIN: - foreach ($languages as $language) { - $host = parse_url($language->domain, PHP_URL_HOST); - if ($host && ($_SERVER['HTTP_HOST'] == $host)) { - return $language; - } + // Execute the language providers in the order they were set up and return the first + // valid language found. + $negotiation = variable_get('language_negotiation', array()); + foreach ($negotiation as $provider) { + $callback = $provider['callback']; + + if (isset($provider['file'])) { + require_once DRUPAL_ROOT .'/'. $provider['file']; + } + + if (function_exists($callback)) { + $language = $callback(); + if (isset($language->language) && isset($languages[$language->language])) { + return $language; } - return language_default(); + } + } - 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 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() { + if (variable_get('cache', CACHE_DISABLED) == CACHE_DISABLED) { + // 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(); + + if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { + $browser_accept = explode(",", $_SERVER['HTTP_ACCEPT_LANGUAGE']); + for ($i = 0; $i < count($browser_accept); $i++) { + // The language part is either a code or a code with a quality. + // We cannot do anything with a * code, so it is skipped. + // If the quality is missing, it is assumed to be 1 according to the RFC. + if (preg_match("!([a-z-]+)(;q=([0-9\\.]+))?!", trim($browser_accept[$i]), $found)) { + $browser_langs[$found[1]] = (isset($found[3]) ? (float) $found[3] : 1.0); } } - if ($mode == LANGUAGE_NEGOTIATION_PATH_DEFAULT) { - // If we did not found the language by prefix, choose the default. - return language_default(); + } + + // Order the codes by quality + 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]; } - break; + } } - // User language. + return FALSE; +} + +/** + * Identify language from the user preferences. + */ +function language_from_user() { + $languages = language_list('enabled'); + $languages = $languages[1]; + + // Request parameter. + if (isset($_GET['language_ui']) && isset($languages[$langcode = $_GET['language_ui']])) { + return $_SESSION['language_ui'] = $languages[$langcode]; + } + // Session parameter. + if (isset($_SESSION['language_ui'])) { + return $_SESSION['language_ui']; + } + // User preference (only for logged users). + global $user; if ($user->uid && isset($languages[$user->language])) { return $languages[$user->language]; } - // Browser accept-language parsing. - if ($language = language_from_browser()) { - return $language; - } - - // Fall back on the default if everything else fails. - return language_default(); + // No language preference from the user. + return FALSE; } /** - * Identify language from the Accept-language HTTP header we got. + * Identify language via URL prefix or domain. */ -function language_from_browser() { - // 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(); - - if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { - $browser_accept = explode(",", $_SERVER['HTTP_ACCEPT_LANGUAGE']); - for ($i = 0; $i < count($browser_accept); $i++) { - // The language part is either a code or a code with a quality. - // We cannot do anything with a * code, so it is skipped. - // If the quality is missing, it is assumed to be 1 according to the RFC. - if (preg_match("!([a-z-]+)(;q=([0-9\\.]+))?!", trim($browser_accept[$i]), $found)) { - $browser_langs[$found[1]] = (isset($found[3]) ? (float) $found[3] : 1.0); - } +function language_from_url() { + $url_language = &drupal_static(__FUNCTION__); + + if (!isset($url_language)) { + $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); + if ($language !== FALSE) { + $url_language = $language; + } + break; + + case LANGUAGE_NEGOTIATION_URL_DOMAIN: + $languages = language_list('enabled'); + $languages = $languages[1]; + foreach ($languages as $language) { + $host = parse_url($language->domain, PHP_URL_HOST); + if ($host && ($_SERVER['HTTP_HOST'] == $host)) { + $url_language = $language; + break; + } + } + break; } } - // Order the codes by quality - arsort($browser_langs); + return $url_language; +} - // Try to find the first preferred language we have +/** + * Parse the given path and return the language identified by the + * prefix and the actual path. + */ +function language_split_prefix($path) { $languages = language_list('enabled'); - foreach ($browser_langs as $langcode => $q) { - if (isset($languages['1'][$langcode])) { - return $languages['1'][$langcode]; + $args = empty($path) ? array() : explode('/', $path); + $prefix = array_shift($args); + // Search prefix within enabled languages. + foreach ($languages[1] as $language) { + if (!empty($language->prefix) && $language->prefix == $prefix) { + // Rebuild $path with the language removed. + return array($language, implode('/', $args)); } } + return array(FALSE, $path); } /** @@ -113,13 +195,8 @@ 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: + 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; @@ -127,14 +204,7 @@ function language_url_rewrite(&$path, &$ } break; - case LANGUAGE_NEGOTIATION_PATH_DEFAULT: - $default = language_default(); - if ($options['language']->language == $default->language) { - break; - } - // Intentionally no break here. - - case LANGUAGE_NEGOTIATION_PATH: + case LANGUAGE_NEGOTIATION_URL_PREFIX: if (!empty($options['language']->prefix)) { $options['prefix'] = $options['language']->prefix . '/'; } 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 27 Aug 2009 23:25:14 -0000 @@ -476,33 +476,143 @@ function locale_languages_delete_form_su * Setting for language negotiation options */ function locale_languages_configure_form() { - $form['language_negotiation'] = array( - '#title' => t('Language negotiation'), + $form['language_negotiation_url'] = array( + '#title' => t('Content 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.") + LANGUAGE_NEGOTIATION_URL_PREFIX => t('Path prefix.'), + LANGUAGE_NEGOTIATION_URL_DOMAIN => t('Domain.'), + ), + '#default_value' => variable_get('language_negotiation_url', LANGUAGE_NEGOTIATION_URL_PREFIX), + '#description' => t('Select which part of the URL will determine the content language.
Modifying this setting may break all incoming URLs and should be used with caution in a production environment.') ); + + $form['weight'] = array('#tree' => TRUE); + $form['enabled'] = array('#tree' => TRUE); + $form['#show_config_path'] = FALSE; + $form['#language_providers'] = language_providers(); + + $enabled_providers = variable_get('language_providers_enabled', array()); + $providers_weight = variable_get('language_providers_weight', array()); + uksort($form['#language_providers'], '_language_providers_weight_compare'); + + foreach ($form['#language_providers'] as $id => $provider) { + $form['weight'][$id] = array( + '#type' => 'weight', + '#default_value' => @$providers_weight[$id], + '#attributes' => array('class' => array('language-provider-weight')), + ); + $form['title'][$id] = array('#markup' => check_plain($provider['title'])); + $form['enabled'][$id] = array('#type' => 'checkbox', '#default_value' => @$enabled_providers[$id]); + if ($id == LANGUAGE_NEGOTIATION_DEFAULT) { + $form['enabled'][$id]['#default_value'] = TRUE; + $form['enabled'][$id]['#attributes'] = array('disabled' => 'disabled'); + } + $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. + $form['#show_config_path'] = TRUE; + } + $form['operation'][$id] = array('#markup' => $config_op); + } + $form['submit'] = array( '#type' => 'submit', '#value' => t('Save settings') ); + + $form['#theme'] = 'locale_languages_configure_form'; + return $form; } /** + * Theme the language configure form. + * + * @ingroup themeable + */ +function theme_locale_languages_configure_form($form) { + $output = drupal_render($form['language_negotiation_url']); + $output .= '
'; + + foreach ($form['title'] as $id => $element) { + // Do not take form control structures. + if (is_array($element) && element_child($id)) { + $row = array( + 'data' => array( + '' . drupal_render($form['title'][$id]) . '', + drupal_render($form['description'][$id]), + drupal_render($form['enabled'][$id]), + drupal_render($form['weight'][$id]), + ), + 'class' => array('draggable'), + ); + if ($form['#show_config_path']) { + $row['data'][] = drupal_render($form['operation'][$id]); + } + $rows[] = $row; + } + } + + $header = array( + array('data' => t('Detection method')), + array('data' => t('Description')), + array('data' => t('Enabled')), + array('data' => t('Weight')), + ); + + // If there is at least one operation enabled show the operation column. + if ($form['#show_config_path']) { + $header[] = array('data' => t('Configure')); + } + + $output .= theme('table', $header, $rows, array('id' => 'language-negotiation-providers')); + $output .= drupal_render_children($form); + + drupal_add_tabledrag('language-negotiation-providers', 'order', 'sibling', 'language-provider-weight'); + + return $output; +} + +/** * Submit function for language negotiation settings. */ function locale_languages_configure_form_submit($form, &$form_state) { - variable_set('language_negotiation', $form_state['values']['language_negotiation']); + $negotiation = array(); + $enabled_providers = $form_state['values']['enabled']; + $enabled_providers[LANGUAGE_NEGOTIATION_DEFAULT] = TRUE; + $providers_weight = $form_state['values']['weight']; + asort($providers_weight); + + foreach ($providers_weight as $id => $weight) { + if ($enabled_providers[$id]) { + $provider = $form['#language_providers'][$id]; + $negotiation[$id] = array('callback' => $provider['callback']); + if (isset($provider['file'])) { + $negotiation[$id]['file'] = $provider['file']; + } + } + } + + variable_set('language_negotiation', $negotiation); + variable_set('language_providers_enabled', $enabled_providers); + variable_set('language_providers_weight', $providers_weight); + variable_set('language_negotiation_url', $form_state['values']['language_negotiation_url']); drupal_set_message(t('Language negotiation configuration saved.')); $form_state['redirect'] = 'admin/config/regional/language'; - return; } + +/** + * Helper function used to compare the language providers' weights. + */ +function _language_providers_weight_compare($v1, $v2) { + $providers_weight = variable_get('language_providers_weight', array()); + @$diff = $providers_weight[$v1] - $providers_weight[$v2]; + return $diff ? $diff : strcmp($v1, $v2); +} + /** * @} 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 27 Aug 2009 22:22:39 -0000 @@ -68,6 +68,9 @@ function locale_uninstall() { variable_del('language_default'); variable_del('language_count'); variable_del('language_negotiation'); + variable_del('language_negotiation_url'); + variable_del('language_providers_enabled'); + variable_del('language_providers_weight'); variable_del('javascript_parsed'); variable_del('language_content_type_default'); variable_del('language_content_type_negotiation'); Index: modules/locale/locale.module =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v retrieving revision 1.256 diff -u -p -r1.256 locale.module --- modules/locale/locale.module 24 Aug 2009 00:14:20 -0000 1.256 +++ modules/locale/locale.module 27 Aug 2009 23:40:28 -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 .= ''; - $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'))) . '

'; @@ -265,13 +257,13 @@ function locale_language_selector_form($ ); // Get language negotiation settings. - $mode = variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE); + $mode = language_negotiation() != 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 +329,48 @@ 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_providers(): + */ +function locale_language_providers() { + $providers = array(); + + $providers[LANGUAGE_NEGOTIATION_CONTENT] = array( + 'title' => t('Content'), + 'description' => t('The content language determined from the URL (Path prefix or domain) is used for the site presentation.'), + 'callback' => 'language_from_url', + ); + + $providers[LANGUAGE_NEGOTIATION_USER] = array( + 'title' => t('User'), + 'description' => t('The site presentation is determined from the user language preferences.'), + 'callback' => 'language_from_user', + ); + + $providers[LANGUAGE_NEGOTIATION_BROWSER] = array( + 'title' => t('Browser'), + 'description' => t('The site presentation is determined from the browser\'s language settings.'), + 'callback' => 'language_from_browser', + ); + + $providers[LANGUAGE_NEGOTIATION_DEFAULT] = array( + 'title' => t('Default'), + 'description' => t('The default language is used for site presentation.'), + 'callback' => 'language_default', + ); + + return $providers; +} + // --------------------------------------------------------------------------------- // Locale core functionality @@ -646,7 +674,7 @@ function locale_block_list() { * 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) { + if (variable_get('language_count', 1) > 1) { $path = drupal_is_front_page() ? '' : $_GET['q']; $languages = language_list('enabled'); $links = array(); Index: modules/locale/locale.test =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.test,v retrieving revision 1.39 diff -u -p -r1.39 locale.test --- modules/locale/locale.test 22 Aug 2009 00:58:53 -0000 1.39 +++ modules/locale/locale.test 28 Aug 2009 00:43:45 -0000 @@ -91,7 +91,8 @@ class LocaleConfigurationTest extends Dr // Ensure we can't delete the default language. $path = 'admin/config/regional/language/delete/' . $langcode; $this->drupalGet($path); - $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.')); + $url = url('admin/config/regional/language', array('absolute' => TRUE, 'prefix' => $langcode . '/')); + $this->assertEqual($this->getUrl(), $url, t('Correct page redirection.')); $this->assertText(t('The default language cannot be deleted.'), t('Failed to delete the default language.')); // Check if we can disable a language. @@ -910,11 +911,11 @@ class LocaleUninstallFunctionalTest exte /** * The default language set for the UI before uninstall. */ - protected $ui_language; + protected $language_ui; function setUp() { parent::setUp('locale'); - $this->ui_language = 'en'; + $this->language_ui = 'en'; } /** @@ -925,15 +926,16 @@ 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->ui_language == 'fr'); + locale_add_language('fr', 'French', 'Français', LANGUAGE_LTR, '', '', TRUE, $this->language_ui == 'fr'); // Check the UI language. drupal_language_initialize(); - global $language; - $this->assertEqual($language->language, $this->ui_language, t('Current language: %lang', array('%lang' => $language->language))); + global $language_ui; + $this->assertEqual($language_ui->language, $this->language_ui, t('Current language: %lang', array('%lang' => $language_ui->language))); // Change language negotiation options. - variable_set('language_negotiation', LANGUAGE_NEGOTIATION_PATH_DEFAULT); + drupal_load('module', 'locale'); + variable_set('language_negotiation', locale_language_providers()); // Enable multilingual workflow option for articles. variable_set('language_content_type_article', 1); @@ -968,7 +970,7 @@ class LocaleUninstallFunctionalTest exte // Check the init language logic. drupal_language_initialize(); - $this->assertEqual($language->language, 'en', t('Language after uninstall: %lang', array('%lang' => $language->language))); + $this->assertEqual($language_ui->language, 'en', t('Language after uninstall: %lang', array('%lang' => $language_ui->language))); // Check JavaScript files deletion. $this->assertTrue($result = !file_exists($js_file), t('JavaScript file deleted: %file', array('%file' => $result ? $js_file : t('found')))); @@ -978,7 +980,7 @@ class LocaleUninstallFunctionalTest exte $this->assertEqual($language_count, 1, t('Language count: %count', array('%count' => $language_count))); // Check language negotiation. - $language_negotiation = variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) == LANGUAGE_NEGOTIATION_NONE; + $language_negotiation = language_negotiation() == LANGUAGE_NEGOTIATION_DEFAULT; $this->assertTrue($language_negotiation, t('Language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set')))); // Check JavaScript parsed. @@ -1018,7 +1020,7 @@ class LocaleUninstallFrenchFunctionalTes function setUp() { parent::setUp(); - $this->ui_language = 'fr'; + $this->language_ui = 'fr'; } } @@ -1060,13 +1062,6 @@ class LanguageSwitchingFunctionalTest ex ); $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); - // Set language negotiation. - $edit = array( - 'language_negotiation' => LANGUAGE_NEGOTIATION_PATH_DEFAULT, - ); - $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); - $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.')); - // Assert that the language switching block is displayed on the frontpage. $this->drupalGet(''); $this->assertText(t('Languages'), t('Language switcher block found.')); @@ -1243,10 +1238,8 @@ class LocalePathFunctionalTest extends D $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); // Set language negotiation. - $edit = array( - 'language_negotiation' => LANGUAGE_NEGOTIATION_PATH_DEFAULT, - ); - $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); + drupal_load('module', 'locale'); + variable_set('language_negotiation', locale_language_providers()); // Create a node. $node = $this->drupalCreateNode(array('type' => 'page')); @@ -1353,10 +1346,8 @@ class LocaleContentFunctionalTest extend $this->drupalPost($path, $edit, t('Save configuration')); // Set language negotiation. - $edit = array( - 'language_negotiation' => LANGUAGE_NEGOTIATION_PATH_DEFAULT, - ); - $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); + drupal_load('module', 'locale'); + variable_set('language_negotiation', locale_language_providers()); // Set page content type to use multilingual support. $this->drupalGet('admin/structure/node-type/page'); @@ -1412,7 +1403,7 @@ class LocaleContentFunctionalTest extend /** * Test UI language negotiation - * 1. LANGUAGE_NEGOTIATION_PATH_DEFAULT + * 1. CONTENT (PATH) > DEFAULT * UI Language base on URL prefix, browser language preference has no * influence: * admin/config @@ -1421,7 +1412,7 @@ class LocaleContentFunctionalTest extend * UI in Chinese * blah-blah/admin/config * 404 - * 2. LANGUAGE_NEGOTIATION_PATH + * 2. CONTENT (PATH) > BROWSER > DEFAULT * admin/config * UI in user's browser language preference if the site has that * language enabled, if not, the default language @@ -1429,7 +1420,7 @@ class LocaleContentFunctionalTest extend * UI in Chinese * blah-blah/admin/config * 404 - * 3. LANGUAGE_NEGOTIATION_DOMAIN + * 3. CONTENT (DOMAIN) > DEFAULT * http://example.com/admin/config * UI language in site default * http://example.cn/admin/config @@ -1446,6 +1437,7 @@ class UILanguageNegotiationTest extends function setUp() { parent::setUp('locale', 'locale_test'); + drupal_load('module', 'locale'); } /** @@ -1475,9 +1467,9 @@ class UILanguageNegotiationTest extends locale_add_language($language_browser_fallback); locale_add_language($language); - // We will look for this string in the admin/config screen to see if the + // We will look for this string in the default node page to see if the // corresponding translated string is shown. - $default_string = 'Configure languages for content and the user interface'; + $default_string = 'Welcome to Drupal'; // Set the default language in order for the translated string to be registered // into database when seen by t(). Without doing this, our target string @@ -1495,8 +1487,9 @@ class UILanguageNegotiationTest extends $language_browser_fallback_string = "In $language_browser_fallback In $language_browser_fallback In $language_browser_fallback"; $language_string = "In $language In $language In $language"; // Do a translate search of our target string. - $edit = array( 'string' => $default_string); + $edit = array('string' => 'Welcome to @site-name'); $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Filter')); + // Should find the string and now click edit to post translated string. $this->clickLink('edit'); $edit = array( @@ -1508,43 +1501,43 @@ class UILanguageNegotiationTest extends $tests = array( // Default, browser preference should have no influence. array( - 'language_negotiation' => LANGUAGE_NEGOTIATION_PATH_DEFAULT, - 'path' => 'admin/config', + 'language_negotiation' => array(LANGUAGE_NEGOTIATION_CONTENT, LANGUAGE_NEGOTIATION_DEFAULT), + 'path' => 'node', 'expect' => $default_string, 'http_header' => $http_header_browser_fallback, - 'message' => 'LANGUAGE_NEGOTIATION_PATH_DEFAULT: no language prefix, UI language is default and not the browser language preference setting is used.', + 'message' => 'CONTENT (PATH) > DEFAULT: no language prefix, UI language is default and not the browser language preference setting is used.', ), // Language prefix. array( - 'language_negotiation' => LANGUAGE_NEGOTIATION_PATH_DEFAULT, - 'path' => "$language/admin/config", + 'language_negotiation' => array(LANGUAGE_NEGOTIATION_CONTENT, LANGUAGE_NEGOTIATION_DEFAULT), + 'path' => "$language/node", 'expect' => $language_string, 'http_header' => $http_header_browser_fallback, - 'message' => 'LANGUAGE_NEGOTIATION_PATH_DEFAULT: with language prefix, UI language is switched based on path prefix', + 'message' => 'CONTENT (PATH) > DEFAULT: with language prefix, UI language is switched based on path prefix', ), // Default, go by browser preference. array( - 'language_negotiation' => LANGUAGE_NEGOTIATION_PATH, - 'path' => 'admin/config', + 'language_negotiation' => array(LANGUAGE_NEGOTIATION_CONTENT, LANGUAGE_NEGOTIATION_BROWSER), + 'path' => 'node', 'expect' => $language_browser_fallback_string, 'http_header' => $http_header_browser_fallback, - 'message' => 'LANGUAGE_NEGOTIATION_PATH: no language prefix, UI language is determined by browser language preference', + 'message' => 'CONTENT (PATH) > BROWSER: no language prefix, UI language is determined by browser language preference', ), // Prefix, switch to the language. array( - 'language_negotiation' => LANGUAGE_NEGOTIATION_PATH, - 'path' => "$language/admin/config", + 'language_negotiation' => array(LANGUAGE_NEGOTIATION_CONTENT, LANGUAGE_NEGOTIATION_BROWSER), + 'path' => "$language/node", 'expect' => $language_string, 'http_header' => $http_header_browser_fallback, - 'message' => 'LANGUAGE_NEGOTIATION_PATH: with langage prefix, UI language is based on path prefix', + 'message' => 'CONTENT (PATH) > BROWSER: with langage prefix, UI language is based on path prefix', ), // Default, browser language preference is not one of site's lang. array( - 'language_negotiation' => LANGUAGE_NEGOTIATION_PATH, - 'path' => 'admin/config', + 'language_negotiation' => array(LANGUAGE_NEGOTIATION_CONTENT, LANGUAGE_NEGOTIATION_BROWSER, LANGUAGE_NEGOTIATION_DEFAULT), + 'path' => 'node', 'expect' => $default_string, 'http_header' => $http_header_blah, - 'message' => 'LANGUAGE_NEGOTIATION_PATH: no language prefix and browser language preference set to unknown language should use default language', + 'message' => 'CONTENT (PATH) > BROWSER > DEFAULT: no language prefix and browser language preference set to unknown language should use default language', ), ); @@ -1553,35 +1546,36 @@ class UILanguageNegotiationTest extends } // Unknown language prefix should return 404. - foreach(array(LANGUAGE_NEGOTIATION_PATH_DEFAULT, LANGUAGE_NEGOTIATION_PATH) as $negotiation) { - variable_set('language_negotiation', $negotiation); - $this->drupalGet("$language_unknown/admin/config", array(), $http_header_browser_fallback); - $this->assertResponse(404, "Unknown language path prefix should return 404, code = $negotiation"); - } + variable_set('language_negotiation', locale_language_providers()); + $this->drupalGet("$language_unknown/admin/config", array(), $http_header_browser_fallback); + $this->assertResponse(404, "Unknown language path prefix should return 404"); // Setup for domain negotiation, first configure the language to have domain // URL. $edit = array('prefix' => '', 'domain' => "http://$language_domain"); $this->drupalPost("admin/config/regional/language/edit/$language", $edit, t('Save language')); // Set the site to use domain language negotiation. - variable_set('language_negotiation', LANGUAGE_NEGOTIATION_DOMAIN); $tests = array( // Default domain, browser preference should have no influence. array( - 'path' => 'admin/config', + 'language_negotiation' => array(LANGUAGE_NEGOTIATION_CONTENT, LANGUAGE_NEGOTIATION_DEFAULT), + 'language_negotiation_url' => LANGUAGE_NEGOTIATION_URL_DOMAIN, + 'path' => 'node', 'expect' => $default_string, 'http_header' => $http_header_browser_fallback, - 'message' => 'LANGUAGE_NEGOTIATION_DOMAIN: default domain should get default language', + 'message' => 'CONTENT (DOMAIN) > DEFAULT: default domain should get default language', ), // Language domain specific URL, we set the $_SERVER['HTTP_HOST'] in // locale_test.module hook_boot() to simulate this. array( + 'language_negotiation' => array(LANGUAGE_NEGOTIATION_CONTENT, LANGUAGE_NEGOTIATION_DEFAULT), + 'language_negotiation_url' => LANGUAGE_NEGOTIATION_URL_DOMAIN, 'locale_test_domain' => $language_domain, - 'path' => 'admin/config', + 'path' => 'node', 'expect' => $language_string, 'http_header' => $http_header_browser_fallback, - 'message' => 'LANGUAGE_NEGOTIATION_DOMAIN: domain example.cn should switch to Chinese', + 'message' => 'CONTENT (DOMAIN) > DEFAULT: domain example.cn should switch to Chinese', ), ); @@ -1592,7 +1586,16 @@ class UILanguageNegotiationTest extends private function runTest($test) { if (!empty($test['language_negotiation'])) { - variable_set('language_negotiation', $test['language_negotiation']); + $negotiation = array(); + foreach (locale_language_providers() as $id => $provider) { + if (in_array($id, $test['language_negotiation'])) { + $negotiation[] = $provider; + } + } + variable_set('language_negotiation', $negotiation); + } + if (!empty($test['language_negotiation_url'])) { + variable_set('language_negotiation_url', $test['language_negotiation_url']); } if (!empty($test['locale_test_domain'])) { variable_set('locale_test_domain', $test['locale_test_domain']); Index: modules/path/path.test =================================================================== RCS file: /cvs/drupal/drupal/modules/path/path.test,v retrieving revision 1.19 diff -u -p -r1.19 path.test --- modules/path/path.test 22 Aug 2009 00:58:54 -0000 1.19 +++ modules/path/path.test 28 Aug 2009 01:14:57 -0000 @@ -183,7 +183,9 @@ class PathLanguageTestCase extends Drupa drupal_static_reset('language_list'); // Set language negotiation to "Path prefix with fallback". - variable_set('language_negotiation', LANGUAGE_NEGOTIATION_PATH); + drupal_load('module', 'locale'); + variable_set('language_negotiation', locale_language_providers()); + variable_set('language_negotiation_url', LANGUAGE_NEGOTIATION_URL_PREFIX); // Force inclusion of language.inc. drupal_language_initialize(); @@ -234,6 +236,10 @@ class PathLanguageTestCase extends Drupa $languages = language_list(); $url = url('node/' . $french_node->nid, array('language' => $languages[$french_node->language])); $this->assertTrue(strpos($url, $edit['path']), t('URL contains the path alias.')); + + // Restore the default language. + variable_set('language_count', 1); + drupal_language_initialize(); } } Index: modules/simpletest/drupal_web_test_case.php =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v retrieving revision 1.145 diff -u -p -r1.145 drupal_web_test_case.php --- modules/simpletest/drupal_web_test_case.php 27 Aug 2009 20:25:28 -0000 1.145 +++ modules/simpletest/drupal_web_test_case.php 28 Aug 2009 00:00:12 -0000 @@ -1797,6 +1797,9 @@ class DrupalWebTestCase extends DrupalTe $n = strlen($base_path); if (substr($path, 0, $n) == $base_path) { $path = substr($path, $n); + if (function_exists('language_split_prefix')) { + list(, $path) = language_split_prefix($path); + } } if (isset($parts['query'])) { $options['query'] = $parts['query'];