diff options
-rw-r--r-- | includes/locale.inc | 53 | ||||
-rw-r--r-- | modules/locale/locale.install | 11 | ||||
-rw-r--r-- | modules/locale/locale.module | 15 | ||||
-rw-r--r-- | modules/locale/locale.test | 49 |
4 files changed, 122 insertions, 6 deletions
diff --git a/includes/locale.inc b/includes/locale.inc index 76861bae7..1019ba432 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -23,6 +23,12 @@ define('LOCALE_LANGUAGE_NEGOTIATION_BROWSER', 'locale-browser'); define('LOCALE_LANGUAGE_NEGOTIATION_INTERFACE', 'locale-interface'); /** + * If no URL language is available language is determined using an already + * detected one. + */ +define('LOCALE_LANGUAGE_NEGOTIATION_URL_FALLBACK', 'locale-url-fallback'); + +/** * The language is set based on the user language settings. */ define('LOCALE_LANGUAGE_NEGOTIATION_USER', 'locale-user'); @@ -211,6 +217,53 @@ function locale_language_from_url($languages) { } /** + * Determines the language to be assigned to URLs when none is detected. + * + * The language negotiation process has a fallback chain that ends with the + * default language provider. Each built-in language type has a separate + * initialization: + * - Interface language, which is the only configurable one, always gets a valid + * value. If no request-specific language is detected, the default language + * will be used. + * - Content language merely inherits the interface language by default. + * - URL language is detected from the requested URL and will be used to rewrite + * URLs appearing in the page being rendered. If no language can be detected, + * there are two possibilities: + * - If the default language has no configured path prefix or domain, then the + * default language is used. This guarantees that (missing) URL prefixes are + * preserved when navigating through the site. + * - If the default language has a configured path prefix or domain, a + * requested URL having an empty prefix or domain is an anomaly that must be + * fixed. This is done by introducing a prefix or domain in the rendered + * page matching the detected interface language. + * + * @param $languages + * (optional) An array of valid language objects. This is passed by + * language_provider_invoke() to every language provider callback, but it is + * not actually needed here. Defaults to NULL. + * @param $language_type + * (optional) The language type to fall back to. Defaults to the interface + * language. + * + * @return + * A valid language code. + */ +function locale_language_url_fallback($language = NULL, $language_type = LANGUAGE_TYPE_INTERFACE) { + $default = language_default(); + $prefix = (variable_get('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX) == LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX); + + // If the default language is not configured to convey language information, + // a missing URL language information indicates that URL language should be + // the default one, otherwise we fall back to an already detected language. + if (($prefix && empty($default->prefix)) || (!$prefix && empty($default->domain))) { + return $default->language; + } + else { + return $GLOBALS[$language_type]->language; + } +} + +/** * Return the URL language switcher block. Translation links may be provided by * other modules. */ diff --git a/modules/locale/locale.install b/modules/locale/locale.install index c51033ea7..cabdc0b34 100644 --- a/modules/locale/locale.install +++ b/modules/locale/locale.install @@ -113,6 +113,17 @@ function locale_update_7001() { } /** + * Updates URL language negotiation by adding the URL fallback detection method. + */ +function locale_update_7002() { + $language_types_info = language_types_info(); + $info = $language_types_info[LANGUAGE_TYPE_URL]; + if (isset($info['fixed'])) { + language_negotiation_set(LANGUAGE_TYPE_URL, array_flip($info['fixed'])); + } +} + +/** * @} End of "defgroup updates-6.x-to-7.x" */ diff --git a/modules/locale/locale.module b/modules/locale/locale.module index d3e961823..696e0afdc 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -503,8 +503,8 @@ function locale_entity_info_alter(&$entity_info) { * 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. + * URL language provider or the URL fallback provider if no language can be + * detected. It is used by l() as the default language if none is specified. */ function locale_language_types_info() { require_once DRUPAL_ROOT . '/includes/locale.inc'; @@ -517,7 +517,7 @@ function locale_language_types_info() { 'fixed' => array(LOCALE_LANGUAGE_NEGOTIATION_INTERFACE), ), LANGUAGE_TYPE_URL => array( - 'fixed' => array(LOCALE_LANGUAGE_NEGOTIATION_URL), + 'fixed' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LOCALE_LANGUAGE_NEGOTIATION_URL_FALLBACK), ), ); } @@ -582,6 +582,15 @@ function locale_language_negotiation_info() { 'description' => t('Use the detected interface language.'), ); + $providers[LOCALE_LANGUAGE_NEGOTIATION_URL_FALLBACK] = array( + 'types' => array(LANGUAGE_TYPE_URL), + 'callbacks' => array('language' => 'locale_language_url_fallback'), + 'file' => $file, + 'weight' => 8, + 'name' => t('URL fallback'), + 'description' => t('Use an already detected language for URLs if none is found.'), + ); + return $providers; } diff --git a/modules/locale/locale.test b/modules/locale/locale.test index 2efa63d82..6bc2fb757 100644 --- a/modules/locale/locale.test +++ b/modules/locale/locale.test @@ -1730,15 +1730,14 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase { parent::setUp('locale', 'locale_test'); require_once DRUPAL_ROOT . '/includes/language.inc'; drupal_load('module', 'locale'); + $admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages', 'administer blocks')); + $this->drupalLogin($admin_user); } /** * Tests for language switching by URL path. */ function testUILanguageNegotiation() { - $admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages')); - $this->drupalLogin($admin_user); - // A few languages to switch to. // This one is unknown, should get the default lang version. $language_unknown = 'blah-blah'; @@ -1892,6 +1891,50 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase { $this->drupalGet($test['path'], array(), $test['http_header']); $this->assertText($test['expect'], $test['message']); } + + /** + * Test URL language detection when the requested URL has no language. + */ + function testUrlLanguageFallback() { + // Add the Italian language. + $language_browser_fallback = 'it'; + locale_add_language($language_browser_fallback); + $languages = language_list(); + + // Enable the path prefix for the default language: this way any unprefixed + // URL must have a valid fallback value. + $edit = array('prefix' => 'en'); + $this->drupalPost('admin/config/regional/language/edit/en', $edit, t('Save language')); + + // Enable browser and URL language detection. + $edit = array( + 'language[enabled][locale-browser]' => TRUE, + 'language[enabled][locale-url]' => TRUE, + 'language[weight][locale-browser]' => -8, + 'language[weight][locale-url]' => -10, + ); + $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); + $this->drupalGet('admin/config/regional/language/configure'); + + // Enable the language switcher block. + $edit = array('blocks[locale_language][region]' => 'sidebar_first'); + $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); + + // Access the front page without specifying any valid URL language prefix + // and having as browser language preference a non-default language. + $http_header = array("Accept-Language: $language_browser_fallback;q=1"); + $this->drupalGet('', array(), $http_header); + + // Check that the language switcher active link matches the given browser + // language. + $args = array(':url' => base_path() . (!empty($GLOBALS['conf']['clean_url']) ? $language_browser_fallback : "?q=$language_browser_fallback")); + $fields = $this->xpath('//div[@id="block-locale-language"]//a[@class="language-link active" and @href=:url]', $args); + $this->assertTrue($fields[0] == $languages[$language_browser_fallback]->native, t('The browser language is the URL active language')); + + // Check that URLs are rewritten using the given browser language. + $fields = $this->xpath('//div[@id="site-name"]//a[@rel="home" and @href=:url]//span', $args); + $this->assertTrue($fields[0] == 'Drupal', t('URLs are rewritten using the browser language.')); + } } /** |