diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/locale/locale.api.php | 101 | ||||
-rw-r--r-- | modules/locale/locale.css | 8 | ||||
-rw-r--r-- | modules/locale/locale.install | 55 | ||||
-rw-r--r-- | modules/locale/locale.module | 186 | ||||
-rw-r--r-- | modules/locale/locale.test | 121 | ||||
-rw-r--r-- | modules/node/node.module | 46 | ||||
-rw-r--r-- | modules/path/path.test | 4 | ||||
-rw-r--r-- | modules/translation/translation.module | 34 |
8 files changed, 442 insertions, 113 deletions
diff --git a/modules/locale/locale.api.php b/modules/locale/locale.api.php index 5d0852b51..b1805e2e0 100644 --- a/modules/locale/locale.api.php +++ b/modules/locale/locale.api.php @@ -25,20 +25,23 @@ function hook_locale($op = 'groups') { } /** - * Perform alterations on translation links. + * Perform alterations on language switcher links. * - * A translation link may need to point to a different path or use a translated - * link text before going through l(), which will just handle the path aliases. + * A language switcher link may need to point to a different path or use a + * translated link text before going through l(), which will just handle the + * path aliases. * * @param $links * Nested array of links keyed by language code. + * @param $type + * The language type the links will switch. * @param $path * The current path. */ -function hook_translation_link_alter(array &$links, $path) { +function hook_language_switch_link_alter(array &$links, $type, $path) { global $language; - if (isset($links[$language])) { + if ($type == LANGUAGE_TYPE_CONTENT && isset($links[$language])) { foreach ($links[$language] as $link) { $link['attributes']['class'][] = 'active-language'; } @@ -46,5 +49,93 @@ function hook_translation_link_alter(array &$links, $path) { } /** + * Allow modules to define their own language types. + * + * @return + * An array of language type definitions. Each language type has an identifier + * key. The language type definition is an associative array that may contain + * the following key-value pairs: + * - "name": The human-readable language type identifier. + * - "description": A description of the language type. + */ +function hook_language_types_info() { + return array( + 'custom_language_type' => array( + 'name' => t('Custom language'), + 'description' => t('A custom language type.'), + ), + ); +} + +/** + * Perform alterations on language types. + * + * @param $language_types + * Array of language type definitions. + */ +function hook_language_types_info_alter(array &$language_types) { + if (isset($language_types['custom_language_type'])) { + $language_types['custom_language_type_custom']['description'] = t('A far better description.'); + } +} + +/** + * Allow modules to define their own language providers. + * + * @return + * An array of language provider definitions. Each language provider has an + * identifier key. The language provider definition is an associative array + * that may contain the following key-value pairs: + * - "types": An array of allowed language types. If a language provider does + * not specify which language types it should be used with, it will be + * available for all the configurable language types. + * - "callbacks": An array of functions that will be called to perform various + * tasks. Possible key-value pairs are: + * - "language": Required. The callback that will determine the language + * value. + * - "switcher": The callback that will determine the language switch links + * associated to the current language provider. + * - "url_rewrite": The callback that will provide URL rewriting. + * - "file": A file that will be included before the callback is invoked; this + * allows callback functions to be in separate files. + * - "weight": The default weight the language provider has. + * - "name": A human-readable identifier. + * - "description": A description of the language provider. + * - "config": An internal path pointing to the language provider + * configuration page. + * - "cache": The value Drupal's page cache should be set to for the current + * language provider to be invoked. + */ +function hook_language_negotiation_info() { + return array( + 'custom_language_provider' => array( + 'callbacks' => array( + 'language' => 'custom_language_provider_callback', + 'switcher' => 'custom_language_switcher_callback', + 'url_rewrite' => 'custom_language_url_rewrite_callback', + ), + 'file' => drupal_get_path('module', 'custom') . '/custom.module', + 'weight' => -4, + 'types' => array('custom_language_type'), + 'name' => t('Custom language provider'), + 'description' => t('This is a custom language provider.'), + 'cache' => CACHE_DISABLED, + ), + ); +} + +/** + * Perform alterations on language providers. + * + * @param $language_providers + * Array of language provider definitions. + */ +function hook_language_negotiation_info_alter(array &$language_providers) { + if (isset($language_providers['custom_language_provider'])) { + $language_providers['custom_language_provider']['config'] = 'admin/config/regional/language/configure/custom-language-provider'; + } +} + +/** * @} End of "addtogroup hooks". */ diff --git a/modules/locale/locale.css b/modules/locale/locale.css index 3923f9ef3..b571a20d1 100644 --- a/modules/locale/locale.css +++ b/modules/locale/locale.css @@ -19,3 +19,11 @@ #locale-translation-filter-form .form-item select.form-select { width: 100%; } + +.language-switcher-locale-session .active a.active { + color: #0062A0; +} + +.language-switcher-locale-session .active a.session-active { + color: #000000; +} diff --git a/modules/locale/locale.install b/modules/locale/locale.install index 73d8814e1..7800cc0f8 100644 --- a/modules/locale/locale.install +++ b/modules/locale/locale.install @@ -41,6 +41,47 @@ 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(LOCALE_LANGUAGE_NEGOTIATION_URL); + break; + + // LANGUAGE_NEGOTIATION_PATH. + case 2: + $negotiation = array(LOCALE_LANGUAGE_NEGOTIATION_URL, LOCALE_LANGUAGE_NEGOTIATION_USER, LOCALE_LANGUAGE_NEGOTIATION_BROWSER); + break; + + // LANGUAGE_NEGOTIATION_DOMAIN. + case 3: + variable_set('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN); + $negotiation = array(LOCALE_LANGUAGE_NEGOTIATION_URL); + 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)); + language_negotiation_set(LANGUAGE_TYPE_URL, array(LOCALE_LANGUAGE_NEGOTIATION_URL => 0)); + + // Unset the old language negotiation system variable. + variable_del('language_negotiation'); + + return array(); +} + +/** * @} End of "defgroup updates-6.x-to-7.x" */ @@ -62,15 +103,23 @@ function locale_uninstall() { // Clear variables. variable_del('language_default'); variable_del('language_count'); - variable_del('language_negotiation'); - variable_del('javascript_parsed'); + variable_del('language_types'); + variable_del('locale_language_negotiation_url_part'); + variable_del('locale_language_negotiation_session_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 (language_types() as $type) { + variable_del("language_negotiation_$type"); + variable_del("locale_language_providers_enabled_$type"); + variable_del("locale_language_providers_weight_$type"); + } foreach (node_type_get_types() as $type => $content_type) { - $setting = variable_del('language_content_type_' . $type); + $setting = variable_del("language_content_type_$type"); } // Switch back to English: with a $language->language value different from 'en' diff --git a/modules/locale/locale.module b/modules/locale/locale.module index f6968c093..e73eaf87b 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -12,6 +12,32 @@ * Gettext portable object files are supported. */ +/** + * The language is determined using a URL language indicator: + * path prefix or domain according to the configuration. + */ +define('LOCALE_LANGUAGE_NEGOTIATION_URL', 'locale-url'); + +/** + * The language is set based on the browser language settings. + */ +define('LOCALE_LANGUAGE_NEGOTIATION_BROWSER', 'locale-browser'); + +/** + * The language is determined using the current content language. + */ +define('LOCALE_LANGUAGE_NEGOTIATION_CONTENT', 'locale-content'); + +/** + * The language is set based on the user language settings. + */ +define('LOCALE_LANGUAGE_NEGOTIATION_USER', 'locale-user'); + +/** + * The language is set based on the request/session parameters. + */ +define('LOCALE_LANGUAGE_NEGOTIATION_SESSION', 'locale-session'); + // --------------------------------------------------------------------------------- // Hook implementations @@ -38,12 +64,10 @@ function locale_help($path, $arg) { case 'admin/config/regional/language/add': return '<p>' . t('Add all languages to be supported by your site. If your desired language is not available in the <em>Language name</em> drop-down, click <em>Custom language</em> 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.') . '</p>'; case 'admin/config/regional/language/configure': - $output = '<p>' . t("Language negotiation settings determine the site's presentation language. Available options include:") . '</p>'; - $output .= '<ul><li>' . t('<strong>None.</strong> The default language is used for site presentation, though users may (optionally) select a preferred language on the <em>My Account</em> page. (User language preferences will be used for site e-mails, if available.)') . '</li>'; - $output .= '<li>' . t('<strong>Path prefix only.</strong> The presentation language is determined by examining the path for a language code or other custom string that matches the path prefix (if any) specified for each language. If a suitable prefix is not identified, the default language is used. <em>Example: "example.com/de/contact" sets presentation language to German based on the use of "de" within the path.</em>') . '</li>'; - $output .= '<li>' . t("<strong>Path prefix with language fallback.</strong> The presentation language is determined by examining the path for a language code or other custom string that matches the path prefix (if any) specified for each language. If a suitable prefix is not identified, the display language is determined by the user's language preferences from the <em>My Account</em> page, or by the browser's language settings. If a presentation language cannot be determined, the default language is used.") . '</li>'; - $output .= '<li>' . t('<strong>Domain name only.</strong> The presentation language is determined by examining the domain used to access the site, and comparing it to the language domain (if any) specified for each language. If a match is not identified, the default language is used. <em>Example: "http://de.example.com/contact" sets presentation language to German based on the use of "http://de.example.com" in the domain.</em>') . '</li></ul>'; - $output .= '<p>' . t('The path prefix or domain name for a language may be set by editing the <a href="@languages">available languages</a>. In the absence of an appropriate match, the site is displayed in the <a href="@languages">default language</a>.', array('@languages' => url('admin/config/regional/language'))) . '</p>'; + $output = '<p>' . t("Language negotiation settings determine the site's content and presentation languages. For both <em>language types</em> there is a list of <em>language detection methods</em> which can be used to configure the desired language negotiation logic. Each detection method can be <em>dragged</em> to gain a higher priority, but it must be <em>enabled</em> to affect the language negotiation process. If a language detection method is applied then all the lower ones are <em>ignored</em>, otherwise the following one will be taken into account. Some lanaguage detection methods provide a configuration page to further specify their behavior. The <em>default</em> detection method is always applied, so anything below it is always ignored. <strong>Modifying this setting may break all incoming URLs and should be used with caution in a production environment.</strong>") . '</p>'; + $output .= '<p>' . t('Available options include:') .'</p>'; + $output .= '<ul><li>' . t('<strong>URL.</strong> The language is determined by examining the URL for a language code, a custom string, or a domain, that matches the ones (if any) specified for each language. The path prefix or domain name for a language may be set by editing the <a href="@languages">available languages</a>. In the absence of an appropriate match, the site is displayed in the <a href="@languages">default language</a>. A configuration is available to choose whether use the path prefix or the domain. <em>Example: "example.com/de/contact" sets language to German based on the use of "de" within the path. "http://de.example.com/contact" sets presentation language to German based on the use of "http://de.example.com" in the domain.</em>', array('@languages' => url('admin/config/regional/language'))) . '</li>'; + $output .= '<li>' . t('<strong>Session.</strong> The language is determined from a request/session parameter. A configuration is available to choose the URL parameter name to be used. <em>Example: "example.com?language=de" sets language to German based on the use of "de" within the "language" parameter.</em>') . '</li></ul>'; return $output; case 'admin/config/regional/translate': $output = '<p>' . 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.') . '</p>'; @@ -103,6 +127,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/session'] = array( + 'title' => 'Session language provider configuration', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('locale_language_providers_session_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', @@ -249,13 +289,13 @@ function locale_language_selector_form(&$form, &$form_state, $user) { ); // Get language negotiation settings. - $mode = variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE); + $mode = language_negotiation_get(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."), ); } @@ -330,12 +370,97 @@ 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_types_info(). + */ +function locale_language_types_info() { + return array( + LANGUAGE_TYPE_CONTENT => array( + 'name' => t('Content'), + 'description' => t('If a piece of content is available in multiple languages, the one matching the <em>content</em> language will be used.'), + ), + LANGUAGE_TYPE_INTERFACE => array( + 'name' => t('Interface'), + 'description' => t('The interface labels will be displayed in the <em>interface</em> language.'), + ), + LANGUAGE_TYPE_URL => array( + 'fixed' => array(LOCALE_LANGUAGE_NEGOTIATION_URL), + ), + ); +} + +/** + * Implement hook_language_negotiation_info(). + */ +function locale_language_negotiation_info() { + $file = 'includes/locale.inc'; + $providers = array(); + + $providers[LOCALE_LANGUAGE_NEGOTIATION_URL] = array( + 'types' => array(LANGUAGE_TYPE_CONTENT, LANGUAGE_TYPE_INTERFACE, LANGUAGE_TYPE_URL), + 'callbacks' => array( + 'language' => 'locale_language_from_url', + 'switcher' => 'locale_language_switcher_url', + 'url_rewrite' => 'locale_language_url_rewrite_url', + ), + 'file' => $file, + 'weight' => -8, + 'name' => t('URL'), + 'description' => t('The language is determined from the URL (Path prefix or domain).'), + 'config' => 'admin/config/regional/language/configure/url', + ); + + $providers[LOCALE_LANGUAGE_NEGOTIATION_SESSION] = array( + 'callbacks' => array( + 'language' => 'locale_language_from_session', + 'switcher' => 'locale_language_switcher_session', + 'url_rewrite' => 'locale_language_url_rewrite_session', + ), + 'file' => $file, + 'weight' => -6, + 'name' => t('Session'), + 'description' => t('The language is determined from a request/session parameter.'), + 'config' => 'admin/config/regional/language/configure/session', + ); + + $providers[LOCALE_LANGUAGE_NEGOTIATION_USER] = array( + 'callbacks' => array('language' => 'locale_language_from_user'), + 'file' => $file, + 'weight' => -4, + 'name' => t('User'), + 'description' => t('The language is determined from the language preference set in the user account.'), + ); + + $providers[LOCALE_LANGUAGE_NEGOTIATION_BROWSER] = array( + 'callbacks' => array('language' => 'locale_language_from_browser'), + 'name' => $file, + 'weight' => -2, + 'cache' => CACHE_DISABLED, + 'name' => t('Browser'), + 'description' => t('The language is determined from the browser\'s language settings.'), + ); + + $providers[LOCALE_LANGUAGE_NEGOTIATION_CONTENT] = array( + 'types' => array(LANGUAGE_TYPE_INTERFACE), + 'callbacks' => array('language' => 'locale_language_from_content'), + 'file' => $file, + 'weight' => 8, + 'name' => t('Content'), + 'description' => t('The interface language is the same as the negotiated content language.'), + ); + + return $providers; +} + // --------------------------------------------------------------------------------- // Locale core functionality @@ -625,39 +750,36 @@ function locale_css_alter(&$css) { * Implement hook_block_info(). */ function locale_block_info() { - $block['language-switcher']['info'] = t('Language switcher'); - // Not worth caching. - $block['language-switcher']['cache'] = DRUPAL_NO_CACHE; + include_once DRUPAL_ROOT . '/includes/language.inc'; + $block = array(); + $info = language_types_info(); + foreach (language_types_configurable() as $type) { + $block[$type] = array( + 'info' => t('Language switcher (@type)', array('@type' => $info[$type]['name'])), + // Not worth caching. + 'cache' => DRUPAL_NO_CACHE, + ); + } return $block; } /** * Implement hook_block_view(). * - * Displays a language switcher. Translation links may be provided by other modules. - * Only show if we have at least two languages and language dependent - * web addresses, so we can actually link to other language versions. + * Displays a language switcher. Only show if we have at least two languages. */ -function locale_block_view($delta = '') { - if (variable_get('language_count', 1) > 1 && variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) != LANGUAGE_NEGOTIATION_NONE) { +function locale_block_view($type) { + if (variable_get('language_count', 1) > 1) { $path = drupal_is_front_page() ? '<front>' : $_GET['q']; - $languages = language_list('enabled'); - $links = array(); - foreach ($languages[1] as $language) { - $links[$language->language] = array( - 'href' => $path, - 'title' => $language->native, - 'language' => $language, - 'attributes' => array('class' => array('language-link')), - ); + $links = language_negotiation_get_switch_links($type, $path); + + if (isset($links->links) && count($links->links > 1)) { + $class = "language-switcher-{$links->provider}"; + $variables = array('links' => $links->links, 'attributes' => array('class' => array($class))); + $block['content'] = theme('links', $variables); + $block['subject'] = t('Languages'); + return $block; } - - // Allow modules to provide translations for specific links. - drupal_alter('translation_link', $links, $path); - - $block['subject'] = t('Languages'); - $block['content'] = theme('links', array('links' => $links, 'attributes' => array())); - return $block; } } diff --git a/modules/locale/locale.test b/modules/locale/locale.test index 0078b2eb3..fc598e296 100644 --- a/modules/locale/locale.test +++ b/modules/locale/locale.test @@ -910,11 +910,11 @@ class LocaleUninstallFunctionalTest extends DrupalWebTestCase { /** * The default language set for the UI before uninstall. */ - protected $ui_language; + protected $language_interface; function setUp() { parent::setUp('locale'); - $this->ui_language = 'en'; + $this->language_interface = 'en'; } /** @@ -925,15 +925,12 @@ class LocaleUninstallFunctionalTest extends DrupalWebTestCase { // 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_interface == '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))); - - // Change language negotiation options. - variable_set('language_negotiation', LANGUAGE_NEGOTIATION_PATH_DEFAULT); + global $language_interface; + $this->assertEqual($language_interface->language, $this->language_interface, t('Current language: %lang', array('%lang' => $language_interface->language))); // Enable multilingual workflow option for articles. variable_set('language_content_type_article', 1); @@ -959,6 +956,17 @@ class LocaleUninstallFunctionalTest extends DrupalWebTestCase { // Disable string caching. variable_set('locale_cache_strings', 0); + // Change language negotiation options. + drupal_load('module', 'locale'); + variable_set('language_types', drupal_language_types() + array('language_custom' => TRUE)); + variable_set('language_negotiation_' . LANGUAGE_TYPE_INTERFACE, locale_language_negotiation_info()); + variable_set('language_negotiation_' . LANGUAGE_TYPE_CONTENT, locale_language_negotiation_info()); + variable_set('language_negotiation_' . LANGUAGE_TYPE_URL, locale_language_negotiation_info()); + + // Change language providers settings. + variable_set('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX); + variable_set('locale_language_negotiation_session_param', TRUE); + // Uninstall Locale. module_disable($locale_module); drupal_uninstall_modules($locale_module); @@ -968,7 +976,7 @@ class LocaleUninstallFunctionalTest extends DrupalWebTestCase { // 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_interface->language, 'en', t('Language after uninstall: %lang', array('%lang' => $language_interface->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,8 +986,17 @@ class LocaleUninstallFunctionalTest extends DrupalWebTestCase { $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; - $this->assertTrue($language_negotiation, t('Language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set')))); + $this->assertTrue(count(language_types()) == count(drupal_language_types()), t('Language types reset')); + $language_negotiation = language_negotiation_get(LANGUAGE_TYPE_INTERFACE) == LANGUAGE_NEGOTIATION_DEFAULT; + $this->assertTrue($language_negotiation, t('Interface language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set')))); + $language_negotiation = language_negotiation_get(LANGUAGE_TYPE_CONTENT) == LANGUAGE_NEGOTIATION_DEFAULT; + $this->assertTrue($language_negotiation, t('Content language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set')))); + $language_negotiation = language_negotiation_get(LANGUAGE_TYPE_URL) == LANGUAGE_NEGOTIATION_DEFAULT; + $this->assertTrue($language_negotiation, t('URL language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set')))); + + // Check language providers settings. + $this->assertFalse(variable_get('locale_language_negotiation_url_part', FALSE), t('URL language provider indicator settings cleared.')); + $this->assertFalse(variable_get('locale_language_negotiation_session_param', FALSE), t('Visit language provider settings cleared.')); // Check JavaScript parsed. $javascript_parsed_count = count(variable_get('javascript_parsed', array())); @@ -1011,14 +1028,14 @@ class LocaleUninstallFrenchFunctionalTest extends LocaleUninstallFunctionalTest public static function getInfo() { return array( 'name' => 'Locale uninstall (FR)', - 'description' => 'Tests the uninstall process using French as UI language.', + 'description' => 'Tests the uninstall process using French as interface language.', 'group' => 'Locale', ); } function setUp() { parent::setUp(); - $this->ui_language = 'fr'; + $this->language_interface = 'fr'; } } @@ -1050,7 +1067,7 @@ class LanguageSwitchingFunctionalTest extends DrupalWebTestCase { function testLanguageBlock() { // Enable the language switching block. $edit = array( - 'locale_language-switcher[region]' => 'sidebar_first', + 'locale_language[region]' => 'sidebar_first', ); $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); @@ -1061,18 +1078,16 @@ class LanguageSwitchingFunctionalTest extends DrupalWebTestCase { $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.')); + drupal_load('module', 'locale'); + include_once DRUPAL_ROOT . '/includes/language.inc'; + language_negotiation_set(LANGUAGE_TYPE_CONTENT, 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-switcher"]'); + list($language_switcher) = $this->xpath('//div[@id="block-locale-language"]'); $links = array( 'active' => array(), 'inactive' => array(), @@ -1243,10 +1258,8 @@ class LocalePathFunctionalTest extends DrupalWebTestCase { $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_' . LANGUAGE_TYPE_CONTENT, locale_language_negotiation_info()); // Create a node. $node = $this->drupalCreateNode(array('type' => 'page')); @@ -1352,12 +1365,6 @@ class LocaleContentFunctionalTest extends DrupalWebTestCase { ); $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')); - // Set page content type to use multilingual support. $this->drupalGet('admin/structure/types/manage/page'); $this->assertText(t('Multilingual support'), t('Multilingual support fieldset present on content type configuration form.')); @@ -1412,7 +1419,7 @@ class LocaleContentFunctionalTest extends DrupalWebTestCase { /** * Test UI language negotiation - * 1. LANGUAGE_NEGOTIATION_PATH_DEFAULT + * 1. URL (PATH) > DEFAULT * UI Language base on URL prefix, browser language preference has no * influence: * admin/config @@ -1421,7 +1428,7 @@ class LocaleContentFunctionalTest extends DrupalWebTestCase { * UI in Chinese * blah-blah/admin/config * 404 - * 2. LANGUAGE_NEGOTIATION_PATH + * 2. URL (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 +1436,7 @@ class LocaleContentFunctionalTest extends DrupalWebTestCase { * UI in Chinese * blah-blah/admin/config * 404 - * 3. LANGUAGE_NEGOTIATION_DOMAIN + * 3. URL (DOMAIN) > DEFAULT * http://example.com/admin/config * UI language in site default * http://example.cn/admin/config @@ -1446,6 +1453,8 @@ class UILanguageNegotiationTest extends DrupalWebTestCase { function setUp() { parent::setUp('locale', 'locale_test'); + require_once DRUPAL_ROOT . '/includes/language.inc'; + drupal_load('module', 'locale'); } /** @@ -1505,46 +1514,49 @@ class UILanguageNegotiationTest extends DrupalWebTestCase { ); $this->drupalPost(NULL, $edit, t('Save translations')); + // Configure URL language rewrite. + variable_set('locale_language_negotiation_url_type', LANGUAGE_TYPE_INTERFACE); + $tests = array( // Default, browser preference should have no influence. array( - 'language_negotiation' => LANGUAGE_NEGOTIATION_PATH_DEFAULT, + 'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT), 'path' => 'admin/config', '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' => 'URL (PATH) > DEFAULT: no language prefix, UI language is default and the browser language preference setting is not used.', ), // Language prefix. array( - 'language_negotiation' => LANGUAGE_NEGOTIATION_PATH_DEFAULT, + 'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT), 'path' => "$language/admin/config", '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' => 'URL (PATH) > DEFAULT: with language prefix, UI language is switched based on path prefix', ), // Default, go by browser preference. array( - 'language_negotiation' => LANGUAGE_NEGOTIATION_PATH, + 'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LOCALE_LANGUAGE_NEGOTIATION_BROWSER), 'path' => 'admin/config', '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' => 'URL (PATH) > BROWSER: no language prefix, UI language is determined by browser language preference', ), // Prefix, switch to the language. array( - 'language_negotiation' => LANGUAGE_NEGOTIATION_PATH, + 'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LOCALE_LANGUAGE_NEGOTIATION_BROWSER), 'path' => "$language/admin/config", 'expect' => $language_string, 'http_header' => $http_header_browser_fallback, - 'message' => 'LANGUAGE_NEGOTIATION_PATH: with langage prefix, UI language is based on path prefix', + 'message' => 'URL (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, + 'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LOCALE_LANGUAGE_NEGOTIATION_BROWSER, LANGUAGE_NEGOTIATION_DEFAULT), 'path' => 'admin/config', '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' => 'URL (PATH) > BROWSER > DEFAULT: no language prefix and browser language preference set to unknown language should use default language', ), ); @@ -1553,35 +1565,36 @@ class UILanguageNegotiationTest extends DrupalWebTestCase { } // 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_' . LANGUAGE_TYPE_INTERFACE, locale_language_negotiation_info()); + $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( + 'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT), + 'locale_language_negotiation_url_part' => LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN, 'path' => 'admin/config', 'expect' => $default_string, 'http_header' => $http_header_browser_fallback, - 'message' => 'LANGUAGE_NEGOTIATION_DOMAIN: default domain should get default language', + 'message' => 'URL (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(LOCALE_LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT), + 'locale_language_negotiation_url_part' => LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN, 'locale_test_domain' => $language_domain, 'path' => 'admin/config', 'expect' => $language_string, 'http_header' => $http_header_browser_fallback, - 'message' => 'LANGUAGE_NEGOTIATION_DOMAIN: domain example.cn should switch to Chinese', + 'message' => 'URL (DOMAIN) > DEFAULT: domain example.cn should switch to Chinese', ), ); @@ -1592,7 +1605,11 @@ class UILanguageNegotiationTest extends DrupalWebTestCase { private function runTest($test) { if (!empty($test['language_negotiation'])) { - variable_set('language_negotiation', $test['language_negotiation']); + $negotiation = array_flip($test['language_negotiation']); + language_negotiation_set(LANGUAGE_TYPE_INTERFACE, $negotiation); + } + if (!empty($test['locale_language_negotiation_url_part'])) { + variable_set('locale_language_negotiation_url_part', $test['locale_language_negotiation_url_part']); } if (!empty($test['locale_test_domain'])) { variable_set('locale_test_domain', $test['locale_test_domain']); diff --git a/modules/node/node.module b/modules/node/node.module index ad30fba0d..2e6a3800c 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -1138,6 +1138,52 @@ function node_build_content($node, $build_mode = 'full') { } /** + * Implement 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('Node'), + 'description' => t('The current node language is used.'), + ); + + return $providers; +} + +/** + * Return the language of the current node. + * + * @param $languages + * An array of valid language objects. + * + * @return + * A valid language code on succes, FALSE otherwise. + */ +function node_language_provider($languages) { + require_once DRUPAL_ROOT . '/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 ($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 diff --git a/modules/path/path.test b/modules/path/path.test index d0abc0a2c..21cbb5ab9 100644 --- a/modules/path/path.test +++ b/modules/path/path.test @@ -183,7 +183,9 @@ class PathLanguageTestCase extends DrupalWebTestCase { drupal_static_reset('language_list'); // Set language negotiation to "Path prefix with fallback". - variable_set('language_negotiation', LANGUAGE_NEGOTIATION_PATH); + include_once DRUPAL_ROOT . '/includes/locale.inc'; + variable_set('language_negotiation_' . LANGUAGE_TYPE_CONTENT, locale_language_negotiation_info()); + variable_set('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX); // Force inclusion of language.inc. drupal_language_initialize(); diff --git a/modules/translation/translation.module b/modules/translation/translation.module index 4684c7f2f..a35dfb86a 100644 --- a/modules/translation/translation.module +++ b/modules/translation/translation.module @@ -170,23 +170,17 @@ function translation_form_alter(&$form, &$form_state, $form_id) { */ function translation_node_view($node, $build_mode) { if (isset($node->tnid) && $translations = translation_node_get_translations($node->tnid)) { - // Do not show link to the same node. - unset($translations[$node->language]); - $languages = language_list(); - foreach ($languages as $langcode => $language) { - if (isset($translations[$langcode])) { - $links["node_translation_$langcode"] = array( - 'title' => $language->native, - 'href' => 'node/' . $translations[$langcode]->nid, - 'language' => $language, - 'attributes' => array('title' => $translations[$langcode]->title, 'class' => array('translation-link')), - ); - $node->content['links']['translation'] = array( - '#theme' => 'links', - '#links' => $links, - '#attributes' => array('class' => array('links', 'inline')), - ); - } + $path = 'node/' . $node->nid; + $links = language_negotiation_get_switch_links(LANGUAGE_TYPE_CONTENT, $path); + if (is_object($links)) { + $links = $links->links; + // Do not show link to the same node. + unset($links[$node->language]); + $node->content['links']['translation'] = array( + '#theme' => 'links', + '#links' => $links, + '#attributes' => array('class' => array('links', 'inline')), + ); } } } @@ -407,12 +401,12 @@ function translation_path_get_translations($path) { } /** - * Implement hook_translation_link_alter(). + * Implement hook_language_switch_link_alter(). * * Replaces links with pointers to translated versions of the content. */ -function translation_translation_link_alter(array &$links, $path) { - if ($paths = translation_path_get_translations($path)) { +function translation_language_switch_links_alter(array &$links, $type, $path) { + if ($type == LANGUAGE_TYPE_CONTENT && $paths = translation_path_get_translations($path)) { foreach ($links as $langcode => $link) { if (isset($paths[$langcode])) { // Translation in a different node. |