Commit 3c2b78d7 authored by Dries's avatar Dries

- Patch #1468930 by dawehner, Gábor Hojtsy, plach, das-peter, fubhy: clean up...

- Patch  #1468930 by dawehner, Gábor Hojtsy, plach, das-peter, fubhy: clean up and move the code for the negotiation functionality from locale module to language module.
parent 1fd81149
......@@ -1324,7 +1324,7 @@ function install_select_language(&$install_state) {
*/
function install_select_language_form($form, &$form_state, $files) {
include_once DRUPAL_ROOT . '/core/includes/standard.inc';
include_once DRUPAL_ROOT . '/core/includes/locale.inc';
include_once DRUPAL_ROOT . '/core/modules/language/language.negotiation.inc';
$standard_languages = standard_language_list();
$select_options = array();
......@@ -1345,7 +1345,7 @@ function install_select_language_form($form, &$form_state, $files) {
);
}
$browser_langcode = locale_language_from_browser($languages);
$browser_langcode = language_from_browser($languages);
$form['langcode'] = array(
'#type' => 'select',
'#options' => $select_options,
......@@ -1477,7 +1477,6 @@ function install_profile_modules(&$install_state) {
* The batch definition, if there are language files to import.
*/
function install_import_translations(&$install_state) {
include_once DRUPAL_ROOT . '/core/includes/locale.inc';
include_once drupal_get_path('module', 'locale') . '/locale.bulk.inc';
$langcode = $install_state['parameters']['langcode'];
......@@ -1773,8 +1772,6 @@ function install_check_requirements($install_state) {
* Forms API array definition for site configuration.
*/
function _install_configure_form($form, &$form_state, &$install_state) {
include_once DRUPAL_ROOT . '/core/includes/locale.inc';
$form['site_information'] = array(
'#type' => 'fieldset',
'#title' => st('Site information'),
......
......@@ -397,7 +397,7 @@ function language_url_split_prefix($path, $languages) {
$prefix = array_shift($args);
// Search prefix within enabled languages.
$prefixes = locale_language_negotiation_url_prefixes();
$prefixes = language_negotiation_url_prefixes();
foreach ($languages as $language) {
if (isset($prefixes[$language->langcode]) && $prefixes[$language->langcode] == $prefix) {
// Rebuild $path with the language removed.
......
This diff is collapsed.
......@@ -144,7 +144,7 @@ function update_prepare_stored_includes() {
$negotiation = variable_get("language_negotiation_$language_type", array());
foreach ($negotiation as $method_id => &$method) {
if (isset($method['file']) && $method['file'] == 'includes/locale.inc') {
$method['file'] = 'core/includes/locale.inc';
$method['file'] = 'core/modules/language/language.negotiation.inc';
}
}
variable_set("language_negotiation_$language_type", $negotiation);
......
......@@ -2708,7 +2708,6 @@ class FieldTranslationsTestCase extends FieldTestCase {
field_create_instance($instance);
$this->instance = field_read_instance('test_entity', $this->field_name, 'test_bundle');
require_once DRUPAL_ROOT . '/core/includes/locale.inc';
for ($i = 0; $i < 3; ++$i) {
$language = (object) array(
'langcode' => 'l' . $i,
......
......@@ -156,13 +156,19 @@ function language_admin_overview_form_submit($form, &$form_state) {
$language->weight = $form_state['values']['languages'][$langcode]['weight'];
if ($language->default || $old_default->langcode == $langcode) {
// Automatically enable the default language and the language
// which was default previously (because we will not get the
// value from that disabled checkbox).
// Automatically enable the default language and the language which was
// default previously (because we will not get the value from that
// disabled checkbox).
$form_state['values']['languages'][$langcode]['enabled'] = 1;
}
$language->enabled = (int) !empty($form_state['values']['languages'][$langcode]['enabled']);
// If the interface language has been disabled make sure that the form
// redirect includes the new default language as a query parameter.
if ($language->enabled == FALSE && $langcode == $GLOBALS['language_interface']->langcode) {
$form_state['redirect'] = array('admin/config/regional/language', array('language' => $languages[$form_state['values']['site_default']]));
}
language_save($language);
}
......@@ -279,7 +285,7 @@ function _language_admin_common_controls(&$form, $language = NULL) {
'#required' => TRUE,
'#description' => t('Direction that text in this language is presented.'),
'#default_value' => @$language->direction,
'#options' => array(LANGUAGE_LTR => t('Left to right'), LANGUAGE_RTL => t('Right to left'))
'#options' => array(LANGUAGE_LTR => t('Left to right'), LANGUAGE_RTL => t('Right to left')),
);
return $form;
}
......@@ -367,7 +373,7 @@ function language_admin_edit_form_validate($form, &$form_state) {
* Process the language editing form submission.
*/
function language_admin_edit_form_submit($form, &$form_state) {
// Prepare a language object for saving
// Prepare a language object for saving.
$languages = language_list();
$langcode = $form_state['values']['langcode'];
$language = $languages[$langcode];
......@@ -388,7 +394,7 @@ function language_admin_delete_form($form, &$form_state, $language) {
drupal_goto('admin/config/regional/language');
}
// For other languages, warn user that data loss is ahead.
// For other languages, warn the user that data loss is ahead.
$languages = language_list();
if (!isset($languages[$langcode])) {
......@@ -436,3 +442,375 @@ function language_admin_predefined_list() {
asort($predefined);
return $predefined;
}
/**
* Builds the configuration form for language negotiation.
*/
function language_negotiation_configure_form() {
language_negotiation_include();
$form = array(
'#submit' => array('language_negotiation_configure_form_submit'),
'#theme' => 'language_negotiation_configure_form',
'#language_types' => language_types_get_configurable(FALSE),
'#language_types_info' => language_types_info(),
'#language_negotiation_info' => language_negotiation_info(),
);
foreach ($form['#language_types'] as $type) {
language_negotiation_configure_form_table($form, $type);
}
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save settings'),
);
return $form;
}
/**
* Builds a language negotiation method configuration table.
*/
function language_negotiation_configure_form_table(&$form, $type) {
$info = $form['#language_types_info'][$type];
$table_form = array(
'#title' => t('@type language detection', array('@type' => $info['name'])),
'#tree' => TRUE,
'#description' => $info['description'],
'#language_negotiation_info' => array(),
'#show_operations' => FALSE,
'weight' => array('#tree' => TRUE),
'enabled' => array('#tree' => TRUE),
);
$negotiation_info = $form['#language_negotiation_info'];
$enabled_methods = variable_get("language_negotiation_$type", array());
$methods_weight = variable_get("language_negotiation_methods_weight_$type", array());
// Add missing data to the methods lists.
foreach ($negotiation_info as $method_id => $method) {
if (!isset($methods_weight[$method_id])) {
$methods_weight[$method_id] = isset($method['weight']) ? $method['weight'] : 0;
}
}
// Order methods list by weight.
asort($methods_weight);
foreach ($methods_weight as $method_id => $weight) {
// A language method might be no more available if the defining module has
// been disabled after the last configuration saving.
if (!isset($negotiation_info[$method_id])) {
continue;
}
$enabled = isset($enabled_methods[$method_id]);
$method = $negotiation_info[$method_id];
// List the method only if the current type is defined in its 'types' key.
// If it is not defined default to all the configurable language types.
$types = array_flip(isset($method['types']) ? $method['types'] : $form['#language_types']);
if (isset($types[$type])) {
$table_form['#language_negotiation_info'][$method_id] = $method;
$method_name = check_plain($method['name']);
$table_form['weight'][$method_id] = array(
'#type' => 'weight',
'#title' => t('Weight for !title language detection method', array('!title' => drupal_strtolower($method_name))),
'#title_display' => 'invisible',
'#default_value' => $weight,
'#attributes' => array('class' => array("language-method-weight-$type")),
);
$table_form['title'][$method_id] = array('#markup' => $method_name);
$table_form['enabled'][$method_id] = array(
'#type' => 'checkbox',
'#title' => t('Enable !title language detection method', array('!title' => drupal_strtolower($method_name))),
'#title_display' => 'invisible',
'#default_value' => $enabled,
);
if ($method_id === LANGUAGE_NEGOTIATION_DEFAULT) {
$table_form['enabled'][$method_id]['#default_value'] = TRUE;
$table_form['enabled'][$method_id]['#attributes'] = array('disabled' => 'disabled');
}
$table_form['description'][$method_id] = array('#markup' => filter_xss_admin($method['description']));
$config_op = array();
if (isset($method['config'])) {
$config_op = array('#type' => 'link', '#title' => t('Configure'), '#href' => $method['config']);
// If there is at least one operation enabled show the operation column.
$table_form['#show_operations'] = TRUE;
}
$table_form['operation'][$method_id] = $config_op;
}
}
$form[$type] = $table_form;
}
/**
* Returns HTML for the language negotiation configuration form.
*
* @param $variables
* An associative array containing:
* - form: A render element representing the form.
*
* @ingroup themeable
*/
function theme_language_negotiation_configure_form($variables) {
$form = $variables['form'];
$output = '';
foreach ($form['#language_types'] as $type) {
$rows = array();
$title = '<label>' . $form[$type]['#title'] . '</label>';
$description = '<div class="description">' . $form[$type]['#description'] . '</div>';
foreach ($form[$type]['title'] as $id => $element) {
// Do not take form control structures.
if (is_array($element) && element_child($id)) {
$row = array(
'data' => array(
'<strong>' . drupal_render($form[$type]['title'][$id]) . '</strong>',
drupal_render($form[$type]['description'][$id]),
drupal_render($form[$type]['enabled'][$id]),
drupal_render($form[$type]['weight'][$id]),
),
'class' => array('draggable'),
);
if ($form[$type]['#show_operations']) {
$row['data'][] = drupal_render($form[$type]['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[$type]['#show_operations']) {
$header[] = array('data' => t('Operations'));
}
$variables = array(
'header' => $header,
'rows' => $rows,
'attributes' => array('id' => "language-negotiation-methods-$type"),
);
$table = theme('table', $variables);
$table .= drupal_render_children($form[$type]);
drupal_add_tabledrag("language-negotiation-methods-$type", 'order', 'sibling', "language-method-weight-$type");
$output .= '<div class="form-item">' . $title . $description . $table . '</div>';
}
$output .= drupal_render_children($form);
return $output;
}
/**
* Submit handler for language negotiation settings.
*/
function language_negotiation_configure_form_submit($form, &$form_state) {
$configurable_types = $form['#language_types'];
foreach ($configurable_types as $type) {
$method_weights = array();
$enabled_methods = $form_state['values'][$type]['enabled'];
$enabled_methods[LANGUAGE_NEGOTIATION_DEFAULT] = TRUE;
$method_weights_input = $form_state['values'][$type]['weight'];
foreach ($method_weights_input as $method_id => $weight) {
if ($enabled_methods[$method_id]) {
$method_weights[$method_id] = $weight;
}
}
language_negotiation_set($type, $method_weights);
variable_set("language_negotiation_methods_weight_$type", $method_weights_input);
}
// Update non-configurable language types and the related language negotiation
// configuration.
language_types_set();
$form_state['redirect'] = 'admin/config/regional/language/detection';
drupal_set_message(t('Language negotiation configuration saved.'));
}
/**
* Builds the URL language negotiation method configuration form.
*/
function language_negotiation_configure_url_form($form, &$form_state) {
global $base_url;
language_negotiation_include();
$form['language_negotiation_url_part'] = array(
'#title' => t('Part of the URL that determines language'),
'#type' => 'radios',
'#options' => array(
LANGUAGE_NEGOTIATION_URL_PREFIX => t('Path prefix'),
LANGUAGE_NEGOTIATION_URL_DOMAIN => t('Domain'),
),
'#default_value' => variable_get('language_negotiation_url_part', LANGUAGE_NEGOTIATION_URL_PREFIX),
);
$form['prefix'] = array(
'#type' => 'fieldset',
'#tree' => TRUE,
'#title' => t('Path prefix configuration'),
'#description' => t('Language codes or other custom text to use as a path prefix for URL language detection. For the default language, this value may be left blank. <strong>Modifying this value may break existing URLs. Use with caution in a production environment.</strong> Example: Specifying "deutsch" as the path prefix code for German results in URLs like "example.com/deutsch/contact".'),
'#states' => array(
'visible' => array(
':input[name="language_negotiation_url_part"]' => array(
'value' => (string) LANGUAGE_NEGOTIATION_URL_PREFIX,
),
),
),
);
$form['domain'] = array(
'#type' => 'fieldset',
'#tree' => TRUE,
'#title' => t('Domain configuration'),
'#description' => t('The domain names to use for these languages. Leave blank for the default language. Use with caution in a production environment.<strong>Modifying this value may break existing URLs. Use with caution in a production environment.</strong> Example: Specifying "de.example.com" as language domain for German will result in an URL like "http://de.example.com/contact".'),
'#states' => array(
'visible' => array(
':input[name="language_negotiation_url_part"]' => array(
'value' => (string) LANGUAGE_NEGOTIATION_URL_DOMAIN,
),
),
),
);
// Get the enabled languages only.
$languages = language_list(TRUE);
$prefixes = language_negotiation_url_prefixes();
$domains = language_negotiation_url_domains();
foreach ($languages as $langcode => $language) {
$form['prefix'][$langcode] = array(
'#type' => 'textfield',
'#title' => t('%language (%langcode) path prefix', array('%language' => $language->name, '%langcode' => $language->langcode)),
'#maxlength' => 64,
'#default_value' => isset($prefixes[$langcode]) ? $prefixes[$langcode] : '',
'#field_prefix' => $base_url . '/' . (variable_get('clean_url', 0) ? '' : '?q='),
);
$form['domain'][$langcode] = array(
'#type' => 'textfield',
'#title' => t('%language (%langcode) domain', array('%language' => $language->name, '%langcode' => $language->langcode)),
'#maxlength' => 128,
'#default_value' => isset($domains[$langcode]) ? $domains[$langcode] : '',
);
}
$form_state['redirect'] = 'admin/config/regional/language/detection';
$form['actions']['#type'] = 'actions';
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save configuration'),
);
return $form;
}
/**
* Validates the URL language negotiation method configuration.
*
* Validate that the prefixes and domains are unique, and make sure that
* the prefix and domain are only blank for the default.
*/
function language_negotiation_configure_url_form_validate($form, &$form_state) {
// Get the enabled languages only.
$languages = language_list(TRUE);
// Count repeated values for uniqueness check.
$count = array_count_values($form_state['values']['prefix']);
foreach ($languages as $langcode => $language) {
$value = $form_state['values']['prefix'][$langcode];
if ($value === '') {
if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LANGUAGE_NEGOTIATION_URL_PREFIX) {
// Throw a form error if the prefix is blank for a non-default language,
// although it is required for selected negotiation type.
form_error($form['prefix'][$langcode], t('The prefix may only be left blank for the default language.'));
}
}
elseif (isset($count[$value]) && $count[$value] > 1) {
// Throw a form error if there are two languages with the same
// domain/prefix.
form_error($form['prefix'][$langcode], t('The prefix for %language, %value, is not unique.', array('%language' => $language->name, '%value' => $value)));
}
}
// Count repeated values for uniqueness check.
$count = array_count_values($form_state['values']['domain']);
foreach ($languages as $langcode => $language) {
$value = $form_state['values']['domain'][$langcode];
if ($value === '') {
if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LANGUAGE_NEGOTIATION_URL_DOMAIN) {
// Throw a form error if the domain is blank for a non-default language,
// although it is required for selected negotiation type.
form_error($form['domain'][$langcode], t('The domain may only be left blank for the default language.'));
}
}
elseif (isset($count[$value]) && $count[$value] > 1) {
// Throw a form error if there are two languages with the same
// domain/domain.
form_error($form['domain'][$langcode], t('The domain for %language, %value, is not unique.', array('%language' => $language->name, '%value' => $value)));
}
}
// Domain names should not contain protocol and/or ports.
foreach ($languages as $langcode => $name) {
$value = $form_state['values']['domain'][$langcode];
if (!empty($value)) {
// Ensure we have exactly one protocol when checking the hostname.
$host = 'http://' . str_replace(array('http://', 'https://'), '', $value);
if (parse_url($host, PHP_URL_HOST) != $value) {
form_error($form['domain'][$langcode], t('The domain for %language may only contain the domain name, not a protocol and/or port.', array('%language' => $name)));
}
}
}
}
/**
* Saves the URL language negotiation method settings.
*/
function language_negotiation_configure_url_form_submit($form, &$form_state) {
// Save selected format (prefix or domain).
variable_set('language_negotiation_url_part', $form_state['values']['language_negotiation_url_part']);
// Save new domain and prefix values.
language_negotiation_url_prefixes_save($form_state['values']['prefix']);
language_negotiation_url_domains_save($form_state['values']['domain']);
drupal_set_message(t('Configuration saved.'));
}
/**
* Builds the session language negotiation method configuration form.
*/
function language_negotiation_configure_session_form($form, &$form_state) {
$form['language_negotiation_session_param'] = array(
'#title' => t('Request/session parameter'),
'#type' => 'textfield',
'#default_value' => variable_get('language_negotiation_session_param', 'language'),
'#description' => t('Name of the request/session parameter used to determine the desired language.'),
);
$form_state['redirect'] = 'admin/config/regional/language/detection';
return system_settings_form($form);
}
name = Language
description = Lets you configure a number of languages to be used on your website.
description = Lets you configure a number of languages to be used on your website and provides language negotiation functionality.
package = Core
version = VERSION
core = 8.x
......
......@@ -7,10 +7,19 @@
/**
* Implements hook_install().
*
* Enable URL language negotiation by default in order to have a basic working
* system on multilingual sites without needing any preliminary configuration.
*/
function language_install() {
// Add the default language to the database too.
language_save(language_default());
// Enable URL language detection for each configurable language type.
require_once DRUPAL_ROOT . '/core/includes/language.inc';
foreach (language_types_get_configurable(FALSE) as $type) {
language_negotiation_set($type, array(LANGUAGE_NEGOTIATION_URL => 0));
}
}
/**
......@@ -21,6 +30,20 @@ function language_uninstall() {
variable_del('language_default');
variable_del('language_count');
// Clear variables.
variable_del('language_types');
variable_del('language_negotiation_url_part');
variable_del('language_negotiation_url_prefixes');
variable_del('language_negotiation_url_domains');
variable_del('language_negotiation_session_param');
variable_del('language_content_type_default');
variable_del('language_content_type_negotiation');
foreach (language_types_get_all() as $type) {
variable_del("language_negotiation_$type");
variable_del("language_negotiation_methods_weight_$type");
}
// Re-initialize the language system so successive calls to t() and other
// functions will not expect languages to be present.
drupal_language_initialize();
......
......@@ -18,6 +18,8 @@ function language_help($path, $arg) {
$output .= '<dl>';
$output .= '<dt>' . t('Configuring the list of languages') . '</dt>';
$output .= '<dd>' . t('<a href="@configure-languages">Configure the list of languages</a> either using the built-in language list or providing any custom languages you wish.', array('@configure-languages' => url('admin/config/regional/language'))) . '</dd>';
$output .= '<dt>' . t('Configuring a multilingual site') . '</dt>';
$output .= '<dd>' . t("Language negotiation allows your site to automatically change language based on the domain or path used for each request. Users may (optionally) select their preferred language on their <em>My account</em> page, and your site can be configured to honor a web browser's preferred language settings. Site content can be translated using the <a href='@content-help'>Content Translation module</a>.", array('@content-help' => url('admin/help/translation'))) . '</dd>';
$output .= '</dl>';
return $output;
......@@ -26,6 +28,20 @@ function language_help($path, $arg) {
case 'admin/config/regional/language/add':
return '<p>' . t('Add a language to be supported by your site. If your desired language is not available, pick <em>Custom language...</em> at the end and provide a language code and other details manually.') . '</p>';
case 'admin/config/regional/language/detection':
$output = '<p>' . t("Define how to decide which language is used to display page elements (primarily text provided by Drupal and modules, such as field labels and help text). This decision is made by evaluating a series of detection methods for languages; the first detection method that gets a result will determine which language is used for that type of text. Define the order of evaluation of language detection methods on this page.") . '</p>';
return $output;
case 'admin/config/regional/language/detection/session':
$output = '<p>' . t('Determine the language from a request/session parameter. Example: "http://example.com?language=de" sets language to German based on the use of "de" within the "language" parameter.') . '</p>';
return $output;
case 'admin/structure/block/manage/%/%':
if ($arg[4] == 'language' && $arg[5] == 'language_interface') {
return '<p>' . t('This block is only shown if <a href="@languages">at least two languages are enabled</a> and <a href="@configuration">language negotiation</a> is set to <em>URL</em> or <em>Session</em>.', array('@languages' => url('admin/config/regional/language'), '@configuration' => url('admin/config/regional/language/detection'))) . '</p>';
}
break;
}
}
......@@ -33,6 +49,7 @@ function language_help($path, $arg) {
* Implements hook_menu().
*/
function language_menu() {
// Base language management and configuration.
$items['admin/config/regional/language'] = array(
'title' => 'Languages',
'description' => 'Configure languages for content and the user interface.',
......@@ -70,6 +87,34 @@ function language_menu() {
'access arguments' => array('administer languages'),
'file' => 'language.admin.inc',
);
// Language negotiation.
$items['admin/config/regional/language/detection'] = array(
'title' => 'Detection and selection',
'page callback' => 'drupal_get_form',
'page arguments' => array('language_negotiation_configure_form'),
'access arguments' => array('administer languages'),
'weight' => 10,
'file' => 'language.admin.inc',
'type' => MENU_LOCAL_TASK,
);
$items['admin/config/regional/language/detection/url'] = array(
'title' => 'URL language detection configuration',
'page callback' => 'drupal_get_form',
'page arguments' => array('language_negotiation_configure_url_form'),
'access arguments' => array('administer languages'),
'file' => 'language.admin.inc',
'type' => MENU_VISIBLE_IN_BREADCRUMB,
);
$items['admin/config/regional/language/detection/session'] = array(
'title' => 'Session language detection configuration',
'page callback' => 'drupal_get_form',
'page arguments' => array('language_negotiation_configure_session_form'),
'access arguments' => array('administer languages'),
'file' => 'language.admin.inc',
'type' => MENU_VISIBLE_IN_BREADCRUMB,
);
return $items;
}
......@@ -97,6 +142,9 @@ function language_theme() {
'render element' => 'elements',
'file' => 'language.admin.inc',
),
'language_negotiation_configure_form' => array(
'render element' => 'form',
),
);