diff --git a/core/core.services.yml b/core/core.services.yml index 1449b3b..e85c346 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -235,7 +235,6 @@ services: arguments: ['@event_dispatcher', '@service_container', '@controller_resolver'] language_manager: class: Drupal\Core\Language\LanguageManager - arguments: ['@state', '@module_handler'] string_translator.custom_strings: class: Drupal\Core\StringTranslation\Translator\CustomStrings arguments: ['@settings'] @@ -243,6 +242,9 @@ services: - { name: string_translator, priority: 30 } string_translation: class: Drupal\Core\StringTranslation\TranslationManager + arguments: ['@language_manager'] + calls: + - [initLanguageManager] database.slave: class: Drupal\Core\Database\Connection factory_class: Drupal\Core\Database\Database @@ -528,11 +530,6 @@ services: tags: - { name: event_subscriber } arguments: ['@config.storage', '@config.storage.snapshot'] - language_request_subscriber: - class: Drupal\Core\EventSubscriber\LanguageRequestSubscriber - tags: - - { name: event_subscriber } - arguments: ['@language_manager', '@string_translation'] exception_controller: class: Drupal\Core\Controller\ExceptionController arguments: ['@content_negotiation', '@string_translation', '@title_resolver', '@html_page_renderer'] diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 1568f45..284822f 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -2331,7 +2331,7 @@ function drupal_installation_attempted() { function drupal_language_initialize() { $language_manager = \Drupal::languageManager(); $language_manager->init(); - \Drupal::translation()->setDefaultLangcode($language_manager->getLanguage(Language::TYPE_INTERFACE)->id); + \Drupal::translation()->setDefaultLangcode($language_manager->getCurrentLanguage()->id); } /** @@ -2343,47 +2343,10 @@ function drupal_language_initialize() { * The type of language object needed, e.g. Language::TYPE_INTERFACE. * * @deprecated as of Drupal 8.0. Use - * \Drupal::languageManager()->getLanguage($type). + * \Drupal::languageManager()->getCurrentLanguage(). */ function language($type) { - return \Drupal::languageManager()->getLanguage($type); -} - -/** - * Returns an array of the available language types. - * - * @return array - * An array of all language types where the keys of each are the language type - * name and its value is its configurability (TRUE/FALSE). - */ -function language_types_get_all() { - $types = \Drupal::config('system.language.types')->get('all'); - return $types ? $types : array_keys(language_types_get_default()); -} - -/** - * Returns a list of the built-in language types. - * - * @return array - * An array of key-values pairs where the key is the language type name and - * the value is its configurability (TRUE/FALSE). - */ -function language_types_get_default() { - return array( - Language::TYPE_INTERFACE => TRUE, - Language::TYPE_CONTENT => FALSE, - Language::TYPE_URL => FALSE, - ); -} - -/** - * Returns TRUE if there is more than one language enabled. - * - * @return bool - * TRUE if more than one language is enabled. - */ -function language_multilingual() { - return \Drupal::languageManager()->isMultilingual(); + return \Drupal::languageManager()->getCurrentLanguage($type); } /** @@ -2397,98 +2360,12 @@ function language_multilingual() { * @return array * An associative array of languages, keyed by the language code, ordered by * weight ascending and name ascending. - */ -function language_list($flags = Language::STATE_CONFIGURABLE) { - - $languages = &drupal_static(__FUNCTION__); - - // Initialize master language list. - if (!isset($languages)) { - // Initialize local language list cache. - $languages = array(); - - // Fill in master language list based on current configuration. - $default = language_default(); - if (language_multilingual() || \Drupal::moduleHandler()->moduleExists('language')) { - // Use language module configuration if available. - $language_entities = config_get_storage_names_with_prefix('language.entity.'); - - // Initialize default property so callers have an easy reference and can - // save the same object without data loss. - foreach ($language_entities as $langcode_config_name) { - $langcode = substr($langcode_config_name, strlen('language.entity.')); - $info = \Drupal::config($langcode_config_name)->get(); - $languages[$langcode] = new Language(array( - 'default' => ($info['id'] == $default->id), - 'name' => $info['label'], - 'id' => $info['id'], - 'direction' => $info['direction'], - 'locked' => $info['locked'], - 'weight' => $info['weight'], - )); - } - Language::sort($languages); - } - else { - // No language module, so use the default language only. - $languages = array($default->id => $default); - // Add the special languages, they will be filtered later if needed. - $languages += language_default_locked_languages($default->weight); - } - } - - // Filter the full list of languages based on the value of the $all flag. By - // default we remove the locked languages, but the caller may request for - // those languages to be added as well. - $filtered_languages = array(); - - // Add the site's default language if flagged as allowed value. - if ($flags & Language::STATE_SITE_DEFAULT) { - $default = isset($default) ? $default : language_default(); - // Rename the default language. - $default->name = t("Site's default language (@lang_name)", array('@lang_name' => $default->name)); - $filtered_languages['site_default'] = $default; - } - - foreach ($languages as $langcode => $language) { - if (($language->locked && !($flags & Language::STATE_LOCKED)) || (!$language->locked && !($flags & Language::STATE_CONFIGURABLE))) { - continue; - } - $filtered_languages[$langcode] = $language; - } - - return $filtered_languages; -} - -/** - * Returns a list of the default locked languages. * - * @param int $weight - * An integer value that is used as the start value for the weights of the - * locked languages. - * - * @return array - * An array of language objects. + * @deprecated as of Drupal 8.0. Use + * \Drupal::languageManager()->getLanguages() instead. */ -function language_default_locked_languages($weight = 0) { - $locked_language = array( - 'default' => FALSE, - 'locked' => TRUE, - 'enabled' => TRUE, - ); - - $languages = array(); - $languages[Language::LANGCODE_NOT_SPECIFIED] = new Language(array( - 'id' => Language::LANGCODE_NOT_SPECIFIED, - 'name' => t('Not specified'), - 'weight' => ++$weight, - ) + $locked_language); - $languages[Language::LANGCODE_NOT_APPLICABLE] = new Language(array( - 'id' => Language::LANGCODE_NOT_APPLICABLE, - 'name' => t('Not applicable'), - 'weight' => ++$weight, - ) + $locked_language); - return $languages; +function language_list($flags = Language::STATE_CONFIGURABLE) { + return \Drupal::languageManager()->getLanguages($flags); } /** @@ -2499,47 +2376,14 @@ function language_default_locked_languages($weight = 0) { * * @return \Drupal\core\Language\Language|null * A fully-populated language object or NULL. - */ -function language_load($langcode) { - $languages = language_list(Language::STATE_ALL); - return isset($languages[$langcode]) ? $languages[$langcode] : NULL; -} - -/** - * Produced the printed name for a language for display. - * - * @param string $langcode - * The language code. - * - * @return string - * The printed name of the language. - */ -function language_name($langcode) { - if ($langcode == Language::LANGCODE_NOT_SPECIFIED) { - return t('None'); - } - - if ($language = language_load($langcode)) { - return $language->name; - } - if (empty($langcode)) { - return t('Unknown'); - } - return t('Unknown (@langcode)', array('@langcode' => $langcode)); -} - -/** - * Checks if a language is locked. * - * @param string $langcode - * The language code. + * @see \Drupal\Core\Language\LanguageManager::getLanguage() * - * @return bool - * Returns whether the language is locked. + * @deprecated as of Drupal 8.0. Use \Drupal::languageManager()->getLanguage() + * instead. */ -function language_is_locked($langcode) { - $language = language_load($langcode); - return ($language ? $language->locked : FALSE); +function language_load($langcode) { + return \Drupal::languageManager()->getLanguage($langcode); } /** @@ -2547,35 +2391,12 @@ function language_is_locked($langcode) { * * @return \Drupal\Core\Language\Language * A language object. - */ -function language_default() { - $info = variable_get('language_default', array( - 'id' => 'en', - 'name' => 'English', - 'direction' => 0, - 'weight' => 0, - 'locked' => 0, - )); - $info['default'] = TRUE; - return new Language($info); -} - -/** - * Stores or retrieves the path derived during language negotiation. * - * @param string $new_path - * The altered path. - * - * @todo Replace this with a path processor in language module. See - * http://drupal.org/node/1888424. + * @deprecated as of Drupal 8.0. Use + * \Drupal::languageManager()->getDefaultLanguage() instead. */ -function _language_resolved_path($new_path = NULL) { - $path = &drupal_static(__FUNCTION__, NULL); - if ($new_path === NULL) { - return $path; - } - $path = $new_path; - return $path; +function language_default() { + return \Drupal::languageManager()->getDefaultLanguage(); } /** diff --git a/core/includes/common.inc b/core/includes/common.inc index 86fd220..90046b4 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -4528,11 +4528,12 @@ function drupal_render_cid_parts($granularity = NULL) { global $theme, $base_root, $user; $cid_parts[] = $theme; - // If Locale is enabled but we have only one language we do not need it as cid - // part. - if (language_multilingual()) { - foreach (language_types_get_configurable() as $language_type) { - $cid_parts[] = language($language_type)->id; + + // If we have only one language enabled we do not need it as cid part. + $language_manager = \Drupal::languageManager(); + if ($language_manager->isMultilingual()) { + foreach ($language_manager->getLanguageTypes() as $type) { + $cid_parts[] = $language_manager->getCurrentLanguage($type)->id; } } diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index e6a5338..970ecc7 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -1,5 +1,6 @@ register('language_manager', 'Drupal\Core\Language\LanguageManager'); + // If we have a language selected and it is not yet saved in the system // (eg. pre-database data screens we are unable to persistently store // the default language), we should set language_default so the proper @@ -374,7 +378,6 @@ function install_begin_request(&$install_state) { else { // @todo Move into a proper Drupal\Core\DependencyInjection\InstallContainerBuilder. $container = new ContainerBuilder(); - $container->register('event_dispatcher', 'Symfony\Component\EventDispatcher\EventDispatcher'); $container->register('config.storage', 'Drupal\Core\Config\InstallStorage'); @@ -399,9 +402,7 @@ function install_begin_request(&$install_state) { ->addArgument(new Reference('config.typed')); // Register the 'language_manager' service. - $container - ->register('language_manager', 'Drupal\Core\Language\LanguageManager') - ->addArgument(NULL); + $container->register('language_manager', 'Drupal\Core\Language\LanguageManager'); // Register the translation services. install_register_translation_service($container); @@ -1525,6 +1526,7 @@ function install_register_translation_service(ContainerBuilder $container) { $container->register('string_translator.custom_strings', 'Drupal\Core\StringTranslation\Translator\CustomStrings') ->addArgument(settings()); $container->register('string_translation', 'Drupal\Core\StringTranslation\TranslationManager') + ->addArgument(new Reference('language_manager')) ->addMethodCall('addTranslator', array(new Reference('string_translator.file_translation'))) ->addMethodCall('addTranslator', array(new Reference('string_translator.custom_strings'))); } @@ -1602,9 +1604,6 @@ function install_select_language(&$install_state) { * @ingroup forms */ function install_select_language_form($form, &$form_state, $files = array()) { - include_once __DIR__ . '/../modules/language/language.module'; - include_once __DIR__ . '/../modules/language/language.negotiation.inc'; - $standard_languages = LanguageManager::getStandardLanguageList(); $select_options = array(); $browser_options = array(); @@ -1616,22 +1615,19 @@ function install_select_language_form($form, &$form_state, $files = array()) { // Select lists based on available language files. foreach ($files as $langcode => $uri) { $select_options[$langcode] = isset($standard_languages[$langcode]) ? $standard_languages[$langcode][1] : $langcode; - $browser_options[$langcode] = new Language(array( - 'id' => $langcode, - )); + $browser_options[] = $langcode; } } else { // Select lists based on all standard languages. foreach ($standard_languages as $langcode => $language_names) { $select_options[$langcode] = $language_names[1]; - $browser_options[$langcode] = new Language(array( - 'id' => $langcode, - )); + $browser_options[] = $langcode; } } - $browser_langcode = language_from_browser($browser_options); + $request = Request::createFromGlobals(); + $browser_langcode = Browser::getLangcode($request->server->get('HTTP_ACCEPT_LANGUAGE'), $browser_options); $form['langcode'] = array( '#type' => 'select', '#title' => t('Choose language'), diff --git a/core/includes/language.inc b/core/includes/language.inc deleted file mode 100644 index 618e3a7..0000000 --- a/core/includes/language.inc +++ /dev/null @@ -1,551 +0,0 @@ -query; - * if ($query->has('q') && strtok($query->get('q'), '/') == 'admin') { - * return language_default()->id; - * } - * return $langcode; - * } - * ?> - * @endcode - * - * For more information, see - * @link http://drupal.org/node/1497272 Language Negotiation API @endlink - */ - -/** - * Chooses a language based on language negotiation method settings. - * - * @param $type - * The language type key to find the language for. - * - * @param $request - * The HttpReqeust object representing the current request. - * - * @return - * The negotiated language object. - */ -function language_types_initialize($type, $request = NULL) { - // Execute the language negotiation methods in the order they were set up and - // return the first valid language found. - $negotiation = variable_get("language_negotiation_$type", array()); - - foreach ($negotiation as $method_id => $method) { - // Skip negotiation methods not appropriate for this type. - if (isset($method['types']) && !in_array($type, $method['types'])) { - continue; - } - $language = language_negotiation_method_invoke($method_id, $method, $request); - if ($language) { - // Remember the method ID used to detect the language. - $language->method_id = $method_id; - return $language; - } - } - - // If no other language was found use the default one. - $language = language_default(); - $language->method_id = LANGUAGE_NEGOTIATION_SELECTED; - return $language; -} - -/** - * Returns information about all defined language types. - * - * @return - * An associative array of language type information arrays keyed by type - * names. Based on information from hook_language_types_info(). - * - * @see hook_language_types_info(). - */ -function language_types_info() { - $language_types = &drupal_static(__FUNCTION__); - - if (!isset($language_types)) { - $language_types = \Drupal::moduleHandler()->invokeAll('language_types_info'); - // Let other modules alter the list of language types. - drupal_alter('language_types_info', $language_types); - } - - return $language_types; -} - -/** - * Returns only the configurable language types. - * - * A language type maybe configurable or fixed. A fixed language type is a type - * whose language negotiation methods are module-defined and not altered through - * the user interface. - * - * @return - * An array of language type names. - */ -function language_types_get_configurable() { - $configurable = \Drupal::config('system.language.types')->get('configurable'); - return $configurable ? $configurable : array(); -} - -/** - * Disables the given language types. - * - * @param $types - * An array of language types. - */ -function language_types_disable($types) { - $configurable = language_types_get_configurable(); - \Drupal::config('system.language.types')->set('configurable', array_diff($configurable, $types))->save(); -} - -/** - * Updates the language type configuration. - * - * @param array $configurable_language_types - * An array of configurable language types. - */ -function language_types_set(array $configurable_language_types) { - // Ensure that we are getting the defined language negotiation information. An - // invocation of \Drupal\Core\Extension\ModuleHandler::install() or - // \Drupal\Core\Extension\ModuleHandler::uninstall() could invalidate the - // cached information. - drupal_static_reset('language_types_info'); - drupal_static_reset('language_negotiation_info'); - - $language_types = array(); - $negotiation_info = language_negotiation_info(); - $language_types_info = language_types_info(); - - foreach ($language_types_info as $type => $info) { - $configurable = in_array($type, $configurable_language_types); - - // Check whether the language type is unlocked. Only the status of unlocked - // language types can be toggled between configurable and non-configurable. - // The default language negotiation settings, if available, are stored in - // $info['fixed']. - if (empty($info['locked'])) { - // If we have a non-locked non-configurable language type without default - // language negotiation settings, we use the values negotiated for the - // interface language which should always be available. - if (!$configurable && !empty($info['fixed'])) { - $method_weights = array(LANGUAGE_NEGOTIATION_INTERFACE); - $method_weights = array_flip($method_weights); - language_negotiation_set($type, $method_weights); - } - } - else { - // Locked language types with default settings are always considered - // non-configurable. In turn if default settings are missing, the language - // type is always considered configurable. - $configurable = empty($info['fixed']); - - // If the language is non-configurable we need to store its language - // negotiation settings. - if (!$configurable) { - $method_weights = array(); - foreach ($info['fixed'] as $weight => $method_id) { - if (isset($negotiation_info[$method_id])) { - $method_weights[$method_id] = $weight; - } - } - language_negotiation_set($type, $method_weights); - } - } - - $language_types[$type] = $configurable; - } - - // Store the language type configuration. - $config = \Drupal::config('system.language.types'); - $config->set('configurable', array_keys(array_filter($language_types)))->save(); - $config->set('all', array_keys($language_types))->save(); - - // Ensure that subsequent calls of language_types_get_configurable() return - // the updated language type information. - drupal_static_reset('language_types_get_configurable'); -} - -/** - * Returns the ID of the language type's first language negotiation method. - * - * @param $type - * The language type. - * - * @return - * The identifier of the first language negotiation method for the given - * language type, or the default method if none exists. - */ -function language_negotiation_method_get_first($type) { - $negotiation = variable_get("language_negotiation_$type", array()); - return empty($negotiation) ? LANGUAGE_NEGOTIATION_SELECTED : key($negotiation); -} - -/** - * Checks whether a language negotiation method is enabled for a language type. - * - * @param $method_id - * The language negotiation method ID. - * @param $type - * (optional) The language type. If none is passed, all the configurable - * language types will be inspected. - * - * @return - * TRUE if the method is enabled for at least one of the given language - * types, or FALSE otherwise. - */ -function language_negotiation_method_enabled($method_id, $type = NULL) { - $language_types = !empty($type) ? array($type) : language_types_get_configurable(); - - foreach ($language_types as $type) { - $negotiation = variable_get("language_negotiation_$type", array()); - if (isset($negotiation[$method_id])) { - return TRUE; - } - } - - return FALSE; -} - -/** - * Returns the language switch links for the given language type. - * - * @param $type - * The language type. - * @param $path - * The internal path the switch links will be relative to. - * - * @return - * A keyed array of links ready to be themed. - */ -function language_negotiation_get_switch_links($type, $path) { - $links = FALSE; - $negotiation = variable_get("language_negotiation_$type", array()); - - foreach ($negotiation as $method_id => $method) { - if (isset($method['callbacks']['language_switch'])) { - if (isset($method['file'])) { - require_once DRUPAL_ROOT . '/' . $method['file']; - } - - $callback = $method['callbacks']['language_switch']; - $result = $callback($type, $path); - - if (!empty($result)) { - // Allow modules to provide translations for specific links. - drupal_alter('language_switch_links', $result, $type, $path); - $links = (object) array('links' => $result, 'method_id' => $method_id); - break; - } - } - } - - return $links; -} - -/** - * Removes any language negotiation methods that are no longer defined. - */ -function language_negotiation_purge() { - // Ensure that we are getting the defined language negotiation information. An - // invocation of \Drupal\Core\Extension\ModuleHandler::install() or - // \Drupal\Core\Extension\ModuleHandler::uninstall() could invalidate the - // cached information. - drupal_static_reset('language_negotiation_info'); - drupal_static_reset('language_types_info'); - - $negotiation_info = language_negotiation_info(); - foreach (language_types_info() as $type => $type_info) { - $weight = 0; - $method_weights = array(); - foreach (variable_get("language_negotiation_$type", array()) as $method_id => $method) { - if (isset($negotiation_info[$method_id])) { - $method_weights[$method_id] = $weight++; - } - } - language_negotiation_set($type, $method_weights); - } -} - -/** - * Saves a list of language negotiation methods for a language type. - * - * @param $type - * The language type. - * @param $method_weights - * An array of language negotiation method weights keyed by method ID. - */ -function language_negotiation_set($type, $method_weights) { - // Save only the necessary fields. - $method_fields = array('callbacks', 'file', 'cache'); - - $negotiation = array(); - $negotiation_info = language_negotiation_info(); - $default_types = language_types_get_configurable(); - - // Order the language negotiation method list by weight. - asort($method_weights); - - foreach ($method_weights as $method_id => $weight) { - if (isset($negotiation_info[$method_id])) { - $method = $negotiation_info[$method_id]; - // If the language negotiation method does not express any preference - // about types, make it available for any configurable type. - $types = array_flip(isset($method['types']) ? $method['types'] : $default_types); - // Check whether the method is defined and has the right type. - if (isset($types[$type])) { - $method_data = array(); - foreach ($method_fields as $field) { - if (isset($method[$field])) { - $method_data[$field] = $method[$field]; - } - } - $negotiation[$method_id] = $method_data; - } - } - } - - variable_set("language_negotiation_$type", $negotiation); -} - -/** - * Returns all defined language negotiation methods. - * - * @return - * An array of language negotiation methods. - */ -function language_negotiation_info() { - $negotiation_info = &drupal_static(__FUNCTION__); - - if (!isset($negotiation_info)) { - // Collect all the module-defined language negotiation methods. - $negotiation_info = \Drupal::moduleHandler()->invokeAll('language_negotiation_info'); - $languages = language_list(); - $selected_language = $languages[language_from_selected($languages)]; - $description = 'Language based on a selected language. '; - $description .= ($selected_language->id == language_default()->id) ? "(Site's default language (@language_name))" : '(@language_name)'; - // Add the default language negotiation method. - $negotiation_info[LANGUAGE_NEGOTIATION_SELECTED] = array( - 'callbacks' => array( - 'negotiation' => 'language_from_selected', - ), - 'weight' => 12, - 'name' => t('Selected language'), - 'description' => t($description, array('@language_name' => $selected_language->name)), - 'config' => 'admin/config/regional/language/detection/selected', - ); - - // Let other modules alter the list of language negotiation methods. - drupal_alter('language_negotiation_info', $negotiation_info); - } - - return $negotiation_info; -} - -/** - * Invokes a language negotiation method and caches the results. - * - * @param $method_id - * The language negotiation method's identifier. - * @param $method - * (optional) An associative array of information about the method to be - * invoked (see hook_language_negotiation_info() for details). If not passed - * in, it will be loaded through language_negotiation_info(). - * - * @param $request - * (optional) The HttpRequest object representing the current request. - * - * @return - * A language object representing the language chosen by the method. - */ -function language_negotiation_method_invoke($method_id, $method = NULL, $request = NULL) { - $results = &drupal_static(__FUNCTION__); - - if (!isset($results[$method_id])) { - global $user; - - $languages = language_list(); - - if (!isset($method)) { - $negotiation_info = language_negotiation_info(); - $method = $negotiation_info[$method_id]; - } - - if (isset($method['file'])) { - require_once DRUPAL_ROOT . '/' . $method['file']; - } - // Check for a cache mode force from settings.php. - if (settings()->get('page_cache_without_database')) { - $cache_enabled = TRUE; - } - else { - drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES, FALSE); - $config = \Drupal::config('system.performance'); - $cache_enabled = $config->get('cache.page.use_internal'); - } - // If the language negotiation method has no cache preference or this is - // satisfied we can execute the callback. - $cache = !isset($method['cache']) || $user->isAuthenticated() || $method['cache'] == $cache_enabled; - $callback = isset($method['callbacks']['negotiation']) ? $method['callbacks']['negotiation'] : FALSE; - $langcode = $cache && function_exists($callback) ? $callback($languages, $request) : FALSE; - $results[$method_id] = isset($languages[$langcode]) ? $languages[$langcode] : FALSE; - } - - // Since objects are resources, we need to return a clone to prevent the - // language negotiation method cache from being unintentionally altered. The - // same methods might be used with different language types based on - // configuration. - return !empty($results[$method_id]) ? clone($results[$method_id]) : $results[$method_id]; -} - - /** - * Identifies language from configuration. - * - * @param $languages - * An array of valid language objects. - * - * @return - * A valid language code on success, FALSE otherwise. - */ -function language_from_selected($languages) { - $langcode = (string) \Drupal::config('language.negotiation')->get('selected_langcode'); - // Replace the site's default langcode by its real value. - if ($langcode == 'site_default') { - $langcode = language_default()->id; - } - return isset($languages[$langcode]) ? $langcode : language_default()->id; -} - -/** - * Splits the given path into prefix and actual path. - * - * Parse the given path and return the language object identified by the prefix - * and the actual path. - * - * @param $path - * The path to split. - * @param $languages - * An array of valid languages. - * - * @return - * An array composed of: - * - A language object corresponding to the identified prefix on success, - * FALSE otherwise. - * - The path without the prefix on success, the given path otherwise. - */ -function language_url_split_prefix($path, $languages) { - $args = empty($path) ? array() : explode('/', $path); - $prefix = array_shift($args); - - // Search prefix within enabled languages. - $prefixes = language_negotiation_url_prefixes(); - foreach ($languages as $language) { - if (isset($prefixes[$language->id]) && $prefixes[$language->id] == $prefix) { - // Rebuild $path with the language removed. - return array($language, implode('/', $args)); - } - } - - return array(FALSE, $path); -} - -/** - * @} End of "language_negotiation" - */ diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 37f04f9..de29b49 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1237,7 +1237,7 @@ function theme_links($variables) { $num_links = count($links); $i = 0; $active = \Drupal::linkGenerator()->getActive(); - $language_url = \Drupal::languageManager()->getLanguage(Language::TYPE_URL); + $language_url = \Drupal::languageManager()->getCurrentLanguage(Language::TYPE_URL); foreach ($links as $key => $link) { $i++; @@ -2092,7 +2092,7 @@ function template_preprocess_html(&$variables) { $variables['html_attributes'] = new Attribute; // HTML element attributes. - $language_interface = \Drupal::service('language_manager')->getLanguage(); + $language_interface = \Drupal::service('language_manager')->getCurrentLanguage(); $variables['html_attributes']['lang'] = $language_interface->id; $variables['html_attributes']['dir'] = $language_interface->direction ? 'rtl' : 'ltr'; diff --git a/core/includes/update.inc b/core/includes/update.inc index b17624f..41eeb5b 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -477,9 +477,7 @@ function update_prepare_stored_includes() { foreach ($language_types as $language_type) { $negotiation = update_variable_get("language_negotiation_$language_type", array()); foreach ($negotiation as &$method) { - if (isset($method['file']) && $method['file'] == 'includes/locale.inc') { - $method['file'] = 'core/modules/language/language.negotiation.inc'; - } + unset($method['file']); } update_variable_set("language_negotiation_$language_type", $negotiation); } @@ -524,9 +522,6 @@ function update_prepare_d8_language() { db_drop_field('languages', 'native'); db_drop_field('languages', 'enabled'); - // Update language count. - \Drupal::state()->set('language_count', db_query('SELECT COUNT(language) FROM {languages}')->fetchField()); - // Rename the languages table to language. db_rename_table('languages', 'language'); @@ -555,7 +550,7 @@ function update_prepare_d8_language() { db_add_field('language', 'locked', $locked_spec); $max_language_weight = db_query('SELECT MAX(weight) FROM {language}')->fetchField(); - $languages = language_default_locked_languages($max_language_weight); + $languages = \Drupal::languageManager()->getDefaultLockedLanguages($max_language_weight); foreach ($languages as $language) { db_insert('language') ->fields(array( @@ -1640,7 +1635,7 @@ function update_language_list($flags = Language::STATE_CONFIGURABLE) { // Fill in master language list based on current configuration. $default = language_default(); - if (language_multilingual() || \Drupal::moduleHandler()->moduleExists('language')) { + if (\Drupal::languageManager()->isMultilingual() || \Drupal::moduleHandler()->moduleExists('language')) { // Use language module configuration if available. We can not use // entity_load_multiple() because this breaks during updates. $language_entities = config_get_storage_names_with_prefix('language.entity.'); @@ -1665,7 +1660,7 @@ function update_language_list($flags = Language::STATE_CONFIGURABLE) { // No language module, so use the default language only. $languages = array($default->id => $default); // Add the special languages, they will be filtered later if needed. - $languages += language_default_locked_languages($default->weight); + $languages += \Drupal::languageManager()->getDefaultLockedLanguages($default->weight); } } diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php index 9c14801..5b8029d 100644 --- a/core/lib/Drupal.php +++ b/core/lib/Drupal.php @@ -520,7 +520,7 @@ public static function translation() { /** * Returns the language manager service. * - * @return \Drupal\Core\Language\LanguageManager + * @return \Drupal\Core\Language\LanguageManagerInterface * The language manager. */ public static function languageManager() { diff --git a/core/lib/Drupal/Component/Utility/Browser.php b/core/lib/Drupal/Component/Utility/Browser.php new file mode 100644 index 0000000..7977b39 --- /dev/null +++ b/core/lib/Drupal/Component/Utility/Browser.php @@ -0,0 +1,141 @@ + $standard_langcode) { + if ($langcode == $browser_langcode) { + $match[1] = $standard_langcode; + } + } + } + // We can safely use strtolower() here, tags are ASCII. + // RFC2616 mandates that the decimal part is no more than three digits, + // so we multiply the qvalue by 1000 to avoid floating point + // comparisons. + $langcode = strtolower($match[1]); + $qvalue = isset($match[2]) ? (float) $match[2] : 1; + // Take the highest qvalue for this langcode. Although the request + // supposedly contains unique langcodes, our mapping possibly resolves + // to the same langcode for different qvalues. Keep the highest. + $browser_langcodes[$langcode] = max( + (int) ($qvalue * 1000), + (isset($browser_langcodes[$langcode]) ? $browser_langcodes[$langcode] : 0) + ); + } + } + + // We should take pristine values from the HTTP headers, but Internet + // Explorer from version 7 sends only specific language tags (eg. fr-CA) + // without the corresponding generic tag (fr) unless explicitly configured. + // In that case, we assume that the lowest value of the specific tags is the + // value of the generic language to be as close to the HTTP 1.1 spec as + // possible. + // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 and + // http://blogs.msdn.com/b/ie/archive/2006/10/17/accept-language-header-for-internet-explorer-7.aspx + asort($browser_langcodes); + foreach ($browser_langcodes as $langcode => $qvalue) { + // For Chinese languages the generic tag is either zh-hans or zh-hant, so + // we need to handle this separately, we can not split $langcode on the + // first occurrence of '-' otherwise we get a non-existing language zh. + // All other languages use a langcode without a '-', so we can safely + // split on the first occurrence of it. + $generic_tag = ''; + if (strlen($langcode) > 7 && (substr($langcode, 0, 7) == 'zh-hant' || substr($langcode, 0, 7) == 'zh-hans')) { + $generic_tag = substr($langcode, 0, 7); + } + else { + $generic_tag = strtok($langcode, '-'); + } + if (!empty($generic_tag) && !isset($browser_langcodes[$generic_tag])) { + // Add the generic langcode, but make sure it has a lower qvalue as the + // more specific one, so the more specific one gets selected if it's + // defined by both the browser and us. + $browser_langcodes[$generic_tag] = $qvalue - 0.1; + } + } + + // Find the enabled language with the greatest qvalue, following the rules + // of RFC 2616 (section 14.4). If several languages have the same qvalue, + // prefer the one with the greatest weight. + $best_match_langcode = FALSE; + $max_qvalue = 0; + foreach ($langcodes as $langcode_case_sensitive) { + // Language tags are case insensitive (RFC2616, sec 3.10). + $langcode = strtolower($langcode_case_sensitive); + + // If nothing matches below, the default qvalue is the one of the wildcard + // language, if set, or is 0 (which will never match). + $qvalue = isset($browser_langcodes['*']) ? $browser_langcodes['*'] : 0; + + // Find the longest possible prefix of the browser-supplied language ('the + // language-range') that matches this site language ('the language tag'). + $prefix = $langcode; + do { + if (isset($browser_langcodes[$prefix])) { + $qvalue = $browser_langcodes[$prefix]; + break; + } + } + while ($prefix = substr($prefix, 0, strrpos($prefix, '-'))); + + // Find the best match. + if ($qvalue > $max_qvalue) { + $best_match_langcode = $langcode_case_sensitive; + $max_qvalue = $qvalue; + } + } + + return $best_match_langcode; + } + +} diff --git a/core/lib/Drupal/Core/Datetime/Date.php b/core/lib/Drupal/Core/Datetime/Date.php index 0510fa4..ffa3f49 100644 --- a/core/lib/Drupal/Core/Datetime/Date.php +++ b/core/lib/Drupal/Core/Datetime/Date.php @@ -11,7 +11,7 @@ use Drupal\Core\Datetime\DrupalDateTime; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Language\Language; -use Drupal\Core\Language\LanguageManager; +use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\StringTranslation\TranslationInterface; /** @@ -36,7 +36,7 @@ class Date { /** * Language manager for retrieving the default langcode when none is specified. * - * @var \Drupal\Core\Language\LanguageManager + * @var \Drupal\Core\Language\LanguageManagerInterface */ protected $languageManager; @@ -67,12 +67,12 @@ class Date { * * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager. - * @param \Drupal\Core\Language\LanguageManager $language_manager + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. * @param \Drupal\Core\StringTranslation\TranslationInterface $translation * The string translation. */ - public function __construct(EntityManagerInterface $entity_manager, LanguageManager $language_manager, TranslationInterface $translation) { + public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, TranslationInterface $translation) { $this->dateFormatStorage = $entity_manager->getStorageController('date_format'); $this->languageManager = $language_manager; $this->stringTranslation = $translation; @@ -119,7 +119,7 @@ public function format($timestamp, $type = 'medium', $format = '', $timezone = N } if (empty($langcode)) { - $langcode = $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id; + $langcode = $this->languageManager->getCurrentLanguage()->id; } // Create a DrupalDateTime object from the timestamp and timezone. diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index 0160119..3e48b75 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -520,6 +520,8 @@ protected function buildContainer() { $container->register('class_loader')->setSynthetic(TRUE); $container->register('kernel', 'Symfony\Component\HttpKernel\KernelInterface')->setSynthetic(TRUE); $container->register('service_container', 'Symfony\Component\DependencyInjection\ContainerInterface')->setSynthetic(TRUE); + // Register the kernel-level config storage. + $container->set('kernel.config.storage', $this->configStorage); $yaml_loader = new YamlFileLoader($container); foreach ($this->serviceYamls as $filename) { $yaml_loader->load($filename); diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php index bffc91b..85574c7 100644 --- a/core/lib/Drupal/Core/Entity/EntityManager.php +++ b/core/lib/Drupal/Core/Entity/EntityManager.php @@ -142,7 +142,7 @@ public function __construct(\Traversable $namespaces, ContainerInterface $contai $this->discovery = new AnnotatedClassDiscovery('Entity', $namespaces, 'Drupal\Core\Entity\Annotation\EntityType'); $this->discovery = new InfoHookDecorator($this->discovery, 'entity_info'); $this->discovery = new AlterDecorator($this->discovery, 'entity_info'); - $this->discovery = new CacheDecorator($this->discovery, 'entity_info:' . $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id, 'cache', CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE)); + $this->discovery = new CacheDecorator($this->discovery, 'entity_info:' . $this->languageManager->getCurrentLanguage()->id, 'cache', CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE)); $this->factory = new DefaultFactory($this->discovery); $this->container = $container; } @@ -327,7 +327,7 @@ public function getAdminRouteInfo($entity_type, $bundle) { public function getFieldDefinitions($entity_type, $bundle = NULL) { if (!isset($this->entityFieldInfo[$entity_type])) { // First, try to load from cache. - $cid = 'entity_field_definitions:' . $entity_type . ':' . $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id; + $cid = 'entity_field_definitions:' . $entity_type . ':' . $this->languageManager->getCurrentLanguage()->id; if ($cache = $this->cache->get($cid)) { $this->entityFieldInfo[$entity_type] = $cache->data; } @@ -426,7 +426,7 @@ public function getBundleInfo($entity_type) { */ public function getAllBundleInfo() { if (!isset($this->bundleInfo)) { - $langcode = $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id; + $langcode = $this->languageManager->getCurrentLanguage()->id; if ($cache = $this->cache->get("entity_bundle_info:$langcode")) { $this->bundleInfo = $cache->data; } @@ -466,7 +466,7 @@ public function getTranslationFromContext(EntityInterface $entity, $langcode = N if ($entity instanceof TranslatableInterface) { if (empty($langcode)) { - $langcode = $this->languageManager->getLanguage(Language::TYPE_CONTENT)->id; + $langcode = $this->languageManager->getCurrentLanguage(Language::TYPE_CONTENT)->id; } // Retrieve language fallback candidates to perform the entity language diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php index d886224..263dec8 100644 --- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php @@ -9,6 +9,7 @@ use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Language\Language; +use Drupal\Core\Language\LanguageManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -56,17 +57,27 @@ class EntityViewBuilder implements EntityControllerInterface, EntityViewBuilderI protected $cacheBin = 'cache'; /** + * The language manager. + * + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager + */ + protected $languageManager; + + /** * Constructs a new EntityViewBuilder. * * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info * The entity information array. * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager service. + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager + * The language manager. */ - public function __construct(EntityTypeInterface $entity_info, EntityManagerInterface $entity_manager) { + public function __construct(EntityTypeInterface $entity_info, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager) { $this->entityType = $entity_info->id(); $this->entityInfo = $entity_info; $this->entityManager = $entity_manager; + $this->languageManager = $language_manager; $this->viewModesInfo = entity_get_view_modes($this->entityType); } @@ -76,7 +87,8 @@ public function __construct(EntityTypeInterface $entity_info, EntityManagerInter public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) { return new static( $entity_info, - $container->get('entity.manager') + $container->get('entity.manager'), + $container->get('language_manager') ); } @@ -185,7 +197,7 @@ public function view(EntityInterface $entity, $view_mode = 'full', $langcode = N */ public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) { if (!isset($langcode)) { - $langcode = language(Language::TYPE_CONTENT)->id; + $langcode = $this->languageManager->getCurrentLanguage(Language::TYPE_CONTENT)->id; } // Build the view modes and display objects. diff --git a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php index 112644f..8e4596f 100644 --- a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php @@ -55,7 +55,7 @@ public function onRespond(FilterResponseEvent $event) { $response->headers->set('X-UA-Compatible', 'IE=edge,chrome=1', FALSE); // Set the Content-language header. - $response->headers->set('Content-language', $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id); + $response->headers->set('Content-language', $this->languageManager->getCurrentLanguage()->id); // Because pages are highly dynamic, set the last-modified time to now // since the page is in fact being regenerated right now. diff --git a/core/lib/Drupal/Core/Language/Language.php b/core/lib/Drupal/Core/Language/Language.php index 8a50157..f0c36ff 100644 --- a/core/lib/Drupal/Core/Language/Language.php +++ b/core/lib/Drupal/Core/Language/Language.php @@ -18,6 +18,23 @@ */ class Language { + /** + * The values to use to instantiate the default language. + * + * @todo Remove once the default language is converted to config. See + * https://drupal.org/node/2108599. + * + * @var array + */ + public static $defaultValues = array( + 'id' => 'en', + 'name' => 'English', + 'direction' => 0, + 'weight' => 0, + 'locked' => 0, + 'default' => TRUE, + ); + // Properties within the Language are set up as the default language. /** diff --git a/core/lib/Drupal/Core/Language/LanguageManager.php b/core/lib/Drupal/Core/Language/LanguageManager.php index e6526f2..0d913b1 100644 --- a/core/lib/Drupal/Core/Language/LanguageManager.php +++ b/core/lib/Drupal/Core/Language/LanguageManager.php @@ -7,245 +7,202 @@ namespace Drupal\Core\Language; -use Drupal\Component\Utility\MapArray; -use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\KeyValueStore\StateInterface; -use Symfony\Component\HttpFoundation\Request; +use Drupal\Component\Utility\String; +use Drupal\Core\StringTranslation\TranslationInterface; /** - * Class responsible for initializing each language type. + * Class responsible for providing language support on language-unaware sites. */ -class LanguageManager { +class LanguageManager implements LanguageManagerInterface { /** - * A request object. + * The string translation service. * - * @var \Symfony\Component\HttpFoundation\Request + * @var \Drupal\Core\StringTranslation\TranslationInterface */ - protected $request; + protected $translation; /** - * The Key/Value Store to use for state. + * An array of all the available languages keyed by language code. * - * @var \Drupal\Core\KeyValueStore\StateInterface + * @var array */ - protected $state = NULL; + protected $languages; /** - * The module handler service. + * The default language object. * - * @var \Drupal\Core\Extension\ModuleHandlerInterface + * @var \Drupal\Core\Language\Language */ - protected $moduleHandler; + protected $defaultLanguage; /** - * An array of language objects keyed by language type. - * - * @var array + * Constructs a new LanguageManager object. */ - protected $languages; + public function __construct() { + $this->defaultLanguage = new Language(Language::$defaultValues); + } /** - * Whether or not the language manager has been initialized. - * - * @var bool + * {@inheritdoc} */ - protected $initialized = FALSE; + function setTranslation(TranslationInterface $translation) { + $this->translation = $translation; + } /** - * Whether already in the process of language initialization. + * Translates a string to the current language or to a given language. * - * @todo This is only needed due to the circular dependency between language - * and config. See http://drupal.org/node/1862202 for the plan to fix this. - * - * @var bool + * @see \Drupal\Core\StringTranslation\TranslationInterface() */ - protected $initializing = FALSE; + protected function t($string, array $args = array(), array $options = array()) { + return $this->translation ? $this->translation->translate($string, $args, $options) : String::format($string, $args); + } /** - * Constructs an LanguageManager object. - * - * @param \Drupal\Core\KeyValueStore\StateInterface $state - * (optional) The state keyvalue store. Defaults to NULL. - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * (optional) The module handler service. Defaults to NULL. + * {@inheritdoc} */ - public function __construct(StateInterface $state = NULL, ModuleHandlerInterface $module_handler = NULL) { - $this->state = $state; - $this->moduleHandler = $module_handler; + public function init() { } /** - * Initializes each language type to a language object. + * {@inheritdoc} */ - public function init() { - if ($this->initialized) { - return; - } - if ($this->isMultilingual()) { - foreach ($this->getLanguageTypes() as $type) { - $this->getLanguage($type); - } - } - $this->initialized = TRUE; + public function isMultilingual() { + return FALSE; } /** - * Sets the $request property and resets all language types. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * The HttpRequest object representing the current request. + * {@inheritdoc} */ - public function setRequest(Request $request) { - $this->request = $request; - $this->reset(); - $this->init(); + public function getLanguageTypes() { + return array(Language::TYPE_INTERFACE, Language::TYPE_CONTENT, Language::TYPE_URL); } /** - * Returns a language object for the given type. - * - * @param string $type - * (optional) The language type, e.g. the interface or the content language. - * Defaults to \Drupal\Core\Language\Language::TYPE_INTERFACE. - * - * @return \Drupal\Core\Language\Language - * A language object for the given type. + * {@inheritdoc} + */ + public function getCurrentLanguage($type = Language::TYPE_INTERFACE) { + return $this->defaultLanguage; + } + + /** + * {@inheritdoc} */ - public function getLanguage($type = Language::TYPE_INTERFACE) { - if (isset($this->languages[$type])) { - return $this->languages[$type]; + public function reset($type = NULL) { + } + + /** + * {@inheritdoc} + */ + public function getDefaultLanguage() { + return $this->defaultLanguage; + } + + /** + * {@inheritdoc} + */ + public function getLanguages($flags = Language::STATE_CONFIGURABLE) { + // Initialize master language list. + if (!isset($this->languages)) { + // No language module, so use the default language only. + $default = $this->getDefaultLanguage(); + $this->languages = array($default->id => $default); + // Add the special languages, they will be filtered later if needed. + $this->languages += $this->getDefaultLockedLanguages($default->weight); } - if ($this->isMultilingual() && $this->request) { - if (!$this->initializing) { - $this->initializing = TRUE; - // @todo Objectify the language system so that we don't have to load an - // include file and call out to procedural code. See - // http://drupal.org/node/1862202 - include_once DRUPAL_ROOT . '/core/includes/language.inc'; - $this->languages[$type] = language_types_initialize($type, $this->request); - $this->initializing = FALSE; - } - else { - // Config has called getLanguage() during initialization of a language - // type. Simply return the default language without setting it on the - // $this->languages property. See the TODO in the docblock for the - // $initializing property. - return $this->getLanguageDefault(); - } + // Filter the full list of languages based on the value of the $all flag. By + // default we remove the locked languages, but the caller may request for + // those languages to be added as well. + $filtered_languages = array(); + + // Add the site's default language if flagged as allowed value. + if ($flags & Language::STATE_SITE_DEFAULT) { + $default = isset($default) ? $default : $this->getDefaultLanguage(); + // Rename the default language. + $default->name = $this->t("Site's default language (@lang_name)", array('@lang_name' => $default->name)); + $filtered_languages['site_default'] = $default; } - else { - $this->languages[$type] = $this->getLanguageDefault(); + + foreach ($this->languages as $id => $language) { + if (($language->locked && ($flags & Language::STATE_LOCKED)) || (!$language->locked && ($flags & Language::STATE_CONFIGURABLE))) { + $filtered_languages[$id] = $language; + } } - return $this->languages[$type]; + + return $filtered_languages; } /** - * Resets the given language type or all types if none specified. - * - * @param string|null $type - * (optional) The language type to reset as a string, e.g., - * Language::TYPE_INTERFACE, or NULL to reset all language types. Defaults - * to NULL. + * {@inheritdoc} */ - public function reset($type = NULL) { - if (!isset($type)) { - $this->languages = array(); - $this->initialized = FALSE; - } - elseif (isset($this->languages[$type])) { - unset($this->languages[$type]); - } + public function getLanguage($langcode) { + $languages = $this->getLanguages(Language::STATE_ALL); + return isset($languages[$langcode]) ? $languages[$langcode] : NULL; } /** - * Returns whether or not the site has more than one language enabled. - * - * @return bool - * TRUE if more than one language is enabled, FALSE otherwise. + * {@inheritdoc} */ - public function isMultilingual() { - if (!isset($this->state)) { - // No state service in install time. - return FALSE; + function getLanguageName($langcode) { + if ($langcode == Language::LANGCODE_NOT_SPECIFIED) { + return $this->t('None'); + } + if ($language = $this->getLanguage($langcode)) { + return $language->name; } - return ($this->state->get('language_count') ?: 1) > 1; + if (empty($langcode)) { + return $this->t('Unknown'); + } + return $this->t('Unknown (@langcode)', array('@langcode' => $langcode)); } /** - * Returns the language fallback candidates for a given context. - * - * @param string $langcode - * (optional) The language of the current context. Defaults to NULL. - * @param array $context - * (optional) An associative array of data that can be useful to determine - * the fallback sequence. The following keys are used in core: - * - langcode: The desired language. - * - operation: The name of the operation indicating the context where - * language fallback is being applied, e.g. 'entity_view'. - * - data: An arbitrary data structure that makes sense in the provided - * context, e.g. an entity. - * - * @return array - * An array of language codes sorted by priority: first values should be - * tried first. + * {@inheritdoc} */ - public function getFallbackCandidates($langcode = NULL, array $context = array()) { - if ($this->isMultilingual()) { - // Get languages ordered by weight, add Language::LANGCODE_NOT_SPECIFIED at - // the end. - $candidates = array_keys(language_list()); - $candidates[] = Language::LANGCODE_NOT_SPECIFIED; - $candidates = MapArray::copyValuesToKeys($candidates); - - // The first candidate should always be the desired language if specified. - if (!empty($langcode)) { - $candidates = array($langcode => $langcode) + $candidates; - } + public function getDefaultLockedLanguages($weight = 0) { + $languages = array(); - // Let other modules hook in and add/change candidates. - $type = 'language_fallback_candidates'; - $types = array(); - if (!empty($context['operation'])) { - $types[] = $type . '_' . $context['operation']; - } - $types[] = $type; - $this->moduleHandler->alter($types, $candidates, $context); - } - else { - $candidates = array(Language::LANGCODE_DEFAULT); - } + $locked_language = array( + 'default' => FALSE, + 'locked' => TRUE, + ); + $languages[Language::LANGCODE_NOT_SPECIFIED] = new Language(array( + 'id' => Language::LANGCODE_NOT_SPECIFIED, + 'name' => $this->t('Not specified'), + 'weight' => ++$weight, + ) + $locked_language); + + $languages[Language::LANGCODE_NOT_APPLICABLE] = new Language(array( + 'id' => Language::LANGCODE_NOT_APPLICABLE, + 'name' => $this->t('Not applicable'), + 'weight' => ++$weight, + ) + $locked_language); - return $candidates; + return $languages; } /** - * Returns an array of the available language types. - * - * @return array() - * An array of all language types. + * {@inheritdoc} */ - protected function getLanguageTypes() { - return language_types_get_all(); + public function isLanguageLocked($langcode) { + $language = $this->getLanguage($langcode); + return ($language ? $language->locked : FALSE); } /** - * Returns a language object representing the site's default language. - * - * @return \Drupal\Core\Language\Language - * A language object. + * {@inheritdoc} + */ + public function getFallbackCandidates($langcode = NULL, array $context = array()) { + return array(Language::LANGCODE_DEFAULT); + } + + /** + * {@inheritdoc} */ - protected function getLanguageDefault() { - $default_info = variable_get('language_default', array( - 'id' => 'en', - 'name' => 'English', - 'direction' => 0, - 'weight' => 0, - 'locked' => 0, - )); - $default_info['default'] = TRUE; - return new Language($default_info); + public function getLanguageSwitchLinks($type, $path) { + return array(); } /** diff --git a/core/lib/Drupal/Core/Language/LanguageManagerInterface.php b/core/lib/Drupal/Core/Language/LanguageManagerInterface.php new file mode 100644 index 0000000..159c39c --- /dev/null +++ b/core/lib/Drupal/Core/Language/LanguageManagerInterface.php @@ -0,0 +1,168 @@ +languageManager->getLanguage(Language::TYPE_INTERFACE); + $language_interface = $this->languageManager->getCurrentLanguage(); $html_attributes = $page->getHtmlAttributes(); $html_attributes['lang'] = $language_interface->id; $html_attributes['dir'] = $language_interface->direction ? 'rtl' : 'ltr'; diff --git a/core/lib/Drupal/Core/Path/AliasManager.php b/core/lib/Drupal/Core/Path/AliasManager.php index 151bb23..a947068 100644 --- a/core/lib/Drupal/Core/Path/AliasManager.php +++ b/core/lib/Drupal/Core/Path/AliasManager.php @@ -96,7 +96,7 @@ public function getSystemPath($path, $path_language = NULL) { // language. If we used a language different from the one conveyed by the // requested URL, we might end up being unable to check if there is a path // alias matching the URL path. - $path_language = $path_language ?: $this->languageManager->getLanguage(Language::TYPE_URL)->id; + $path_language = $path_language ?: $this->languageManager->getCurrentLanguage(Language::TYPE_URL)->id; // Lookup the path alias first. if (!empty($path) && $source = $this->lookupPathSource($path, $path_language)) { $path = $source; @@ -113,7 +113,7 @@ public function getPathAlias($path, $path_language = NULL) { // language. If we used a language different from the one conveyed by the // requested URL, we might end up being unable to check if there is a path // alias matching the URL path. - $path_language = $path_language ?: $this->languageManager->getLanguage(Language::TYPE_URL)->id; + $path_language = $path_language ?: $this->languageManager->getCurrentLanguage(Language::TYPE_URL)->id; $result = $path; if (!empty($path) && $alias = $this->lookupPathAlias($path, $path_language)) { $result = $alias; diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php index ce93bab..712c5ee 100644 --- a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php +++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php @@ -14,7 +14,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Language\LanguageManager; +use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Language\Language; use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery; use Drupal\Core\Plugin\Factory\ContainerFactory; @@ -84,7 +84,7 @@ class DefaultPluginManager extends PluginManagerBase implements PluginManagerInt /** * The language manager. * - * @var \Drupal\Core\Language\LanguageManager + * @var \Drupal\Core\Language\LanguageManagerInterface */ protected $languageManager; @@ -115,7 +115,7 @@ public function __construct($subdir, \Traversable $namespaces, $plugin_definitio * * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend * Cache backend instance to use. - * @param \Drupal\Core\Language\LanguageManager + * @param \Drupal\Core\Language\LanguageManagerInterface * The language manager. * @param string $cache_key_prefix * Cache key prefix to use, the language code will be appended @@ -129,11 +129,11 @@ public function __construct($subdir, \Traversable $namespaces, $plugin_definitio * clearCachedDefinitions() method. Only use cache tags when cached plugin * definitions should be cleared along with other, related cache entries. */ - public function setCacheBackend(CacheBackendInterface $cache_backend, LanguageManager $language_manager, $cache_key_prefix, array $cache_tags = array()) { + public function setCacheBackend(CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, $cache_key_prefix, array $cache_tags = array()) { $this->languageManager = $language_manager; $this->cacheBackend = $cache_backend; $this->cacheKeyPrefix = $cache_key_prefix; - $this->cacheKey = $cache_key_prefix . ':' . $language_manager->getLanguage(Language::TYPE_INTERFACE)->id; + $this->cacheKey = $cache_key_prefix . ':' . $language_manager->getCurrentLanguage()->id; $this->cacheTags = $cache_tags; } @@ -186,14 +186,16 @@ public function clearCachedDefinitions() { // Use the cache tags to clear the cache. $this->cacheBackend->deleteTags($this->cacheTags); } - else { + elseif ($this->languageManager) { $cache_keys = array(); - // @todo: Use $this->languageManager->languageList() after http://drupal.org/node/1862202 is in. - foreach (language_list() as $langcode => $language) { - $cache_keys[] = $this->cacheKeyPrefix . ':' .$langcode; + foreach ($this->languageManager->getLanguages() as $langcode => $language) { + $cache_keys[] = $this->cacheKeyPrefix . ':' . $langcode; } $this->cacheBackend->deleteMultiple($cache_keys); } + else { + $this->cacheBackend->delete($this->cacheKey); + } } $this->definitions = NULL; } diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationManager.php b/core/lib/Drupal/Core/StringTranslation/TranslationManager.php index 191ece4..bd1b3b3 100644 --- a/core/lib/Drupal/Core/StringTranslation/TranslationManager.php +++ b/core/lib/Drupal/Core/StringTranslation/TranslationManager.php @@ -7,8 +7,9 @@ namespace Drupal\Core\StringTranslation; -use Drupal\Core\StringTranslation\Translator\TranslatorInterface; use Drupal\Component\Utility\String; +use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\Core\StringTranslation\Translator\TranslatorInterface; /** * Defines a chained translation implementation combining multiple translators. @@ -16,6 +17,13 @@ class TranslationManager implements TranslationInterface, TranslatorInterface { /** + * The language manager. + * + * @var \Drupal\Core\Language\LanguageManagerInterface + */ + protected $languageManager; + + /** * An array of active translators keyed by priority. * * @var array @@ -46,11 +54,24 @@ class TranslationManager implements TranslationInterface, TranslatorInterface { /** * Constructs a TranslationManager object. + * + * @param \Drupal\Core\Language\LanguageManagerInterface + * The language manager. + */ + public function __construct(LanguageManagerInterface $language_manager) { + $this->languageManager = $language_manager; + $this->defaultLangcode = $language_manager->getDefaultLanguage()->id; + } + + /** + * Initializes the injected language manager with the translation manager. + * + * This should be called right after instantiating the translation manager to + * make it available to the language manager without introducing a circular + * dependency. */ - public function __construct() { - // @todo Inject language_manager or config system after language_default - // variable is converted to CMI. - $this->defaultLangcode = language_default()->id; + public function initLanguageManager() { + $this->languageManager->setTranslation($this); } /** diff --git a/core/lib/Drupal/Core/StringTranslation/Translator/CustomStrings.php b/core/lib/Drupal/Core/StringTranslation/Translator/CustomStrings.php index 3debb1f..667c3eb 100644 --- a/core/lib/Drupal/Core/StringTranslation/Translator/CustomStrings.php +++ b/core/lib/Drupal/Core/StringTranslation/Translator/CustomStrings.php @@ -38,7 +38,7 @@ public function __construct(Settings $settings) { /** * {@inheritdoc} */ - protected function loadLanguage($langcode) { + protected function getLanguage($langcode) { return $this->settings->get('locale_custom_strings_' . $langcode, array()); } diff --git a/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php b/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php index 3e162b1..ecd5a61 100644 --- a/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php +++ b/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php @@ -41,7 +41,7 @@ public function __construct($directory) { /** * {@inheritdoc} */ - protected function loadLanguage($langcode) { + protected function getLanguage($langcode) { // If the given langcode was selected, there should be at least one .po // file with its name in the pattern drupal-$version.$langcode.po. // This might or might not be the entire filename. It is also possible diff --git a/core/lib/Drupal/Core/StringTranslation/Translator/StaticTranslation.php b/core/lib/Drupal/Core/StringTranslation/Translator/StaticTranslation.php index fd72f5e..1b88a7a 100644 --- a/core/lib/Drupal/Core/StringTranslation/Translator/StaticTranslation.php +++ b/core/lib/Drupal/Core/StringTranslation/Translator/StaticTranslation.php @@ -37,7 +37,7 @@ public function __construct($translations = array()) { */ public function getStringTranslation($langcode, $string, $context) { if (!isset($this->translations[$langcode])) { - $this->translations[$langcode] = $this->loadLanguage($langcode); + $this->translations[$langcode] = $this->getLanguage($langcode); } if (isset($this->translations[$langcode][$context][$string])) { return $this->translations[$langcode][$context][$string]; @@ -60,7 +60,7 @@ public function reset() { * @param string $langcode * The langcode of the language. */ - protected function loadLanguage($langcode) { + protected function getLanguage($langcode) { // This class is usually a base class but we do not declare as abstract // because it can be used on its own, by passing a simple array on the // constructor. This can be useful while testing, but it does not support diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php index 3f63620..1628e63 100644 --- a/core/lib/Drupal/Core/Utility/LinkGenerator.php +++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php @@ -10,7 +10,7 @@ use Drupal\Component\Utility\String; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\Language; -use Drupal\Core\Language\LanguageManager; +use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Template\Attribute; use Drupal\Core\Routing\UrlGeneratorInterface; use Symfony\Cmf\Component\Routing\RouteObjectInterface; @@ -45,7 +45,7 @@ class LinkGenerator implements LinkGeneratorInterface { /** * The language manager. * - * @var \Drupal\Core\Language\LanguageManager + * @var \Drupal\Core\Language\LanguageManagerInterface */ protected $languageManager; @@ -56,10 +56,10 @@ class LinkGenerator implements LinkGeneratorInterface { * The url generator. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. - * @param \Drupal\Core\Language\LanguageManager $language_manager + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. */ - public function __construct(UrlGeneratorInterface $url_generator, ModuleHandlerInterface $module_handler, LanguageManager $language_manager) { + public function __construct(UrlGeneratorInterface $url_generator, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager) { $this->urlGenerator = $url_generator; $this->moduleHandler = $module_handler; $this->languageManager = $language_manager; @@ -79,7 +79,7 @@ public function setRequest(Request $request) { $parameters = $raw_variables ? $raw_variables->all() : array(); $this->active = array( 'route_name' => $request->attributes->get(RouteObjectInterface::ROUTE_NAME), - 'language' => $this->languageManager->getLanguage(Language::TYPE_URL)->id, + 'language' => $this->languageManager->getCurrentLanguage(Language::TYPE_URL)->id, 'parameters' => $parameters + (array) $request->query->all(), ); } diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php index 3befde0..b3d2e61 100644 --- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php @@ -111,7 +111,7 @@ public function form(array $form, array &$form_state) { // Set the correct default language. if ($block->isNew()) { - $language_default = $this->languageManager->getLanguage($language_configuration['langcode']); + $language_default = $this->languageManager->getCurrentLanguage($language_configuration['langcode']); $block->langcode->value = $language_default->id; } } diff --git a/core/modules/block/lib/Drupal/block/BlockFormController.php b/core/modules/block/lib/Drupal/block/BlockFormController.php index 92bbe88..fba5659 100644 --- a/core/modules/block/lib/Drupal/block/BlockFormController.php +++ b/core/modules/block/lib/Drupal/block/BlockFormController.php @@ -13,7 +13,8 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\Query\QueryFactory; use Drupal\Core\Language\Language; -use Drupal\Core\Language\LanguageManager; +use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\language\ConfigurableLanguageManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -45,7 +46,7 @@ class BlockFormController extends EntityFormController { /** * The language manager. * - * @var \Drupal\Core\Language\LanguageManager + * @var \Drupal\Core\Language\LanguageManagerInterface */ protected $languageManager; @@ -63,12 +64,12 @@ class BlockFormController extends EntityFormController { * The entity manager. * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query_factory * The entity query factory. - * @param \Drupal\Core\Language\LanguageManager $language_manager + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. * @param \Drupal\Core\Config\ConfigFactory $config_factory * The config factory. */ - public function __construct(EntityManagerInterface $entity_manager, QueryFactory $entity_query_factory, LanguageManager $language_manager, ConfigFactory $config_factory) { + public function __construct(EntityManagerInterface $entity_manager, QueryFactory $entity_query_factory, LanguageManagerInterface $language_manager, ConfigFactory $config_factory) { $this->storageController = $entity_manager->getStorageController('block'); $this->entityQueryFactory = $entity_query_factory; $this->languageManager = $language_manager; @@ -168,11 +169,11 @@ public function form(array $form, array &$form_state) { } // Configure the block visibility per language. - if ($this->moduleHandler->moduleExists('language') && $this->languageManager->isMultilingual()) { - $configurable_language_types = language_types_get_configurable(); + if ($this->languageManager->isMultilingual() && $this->languageManager instanceof ConfigurableLanguageManagerInterface) { + $language_types = $this->languageManager->getLanguageTypes(); // Fetch languages. - $languages = language_list(Language::STATE_ALL); + $languages = $this->languageManager->getLanguages(Language::STATE_ALL); $langcodes_options = array(); foreach ($languages as $language) { // @todo $language->name is not wrapped with t(), it should be replaced @@ -189,16 +190,16 @@ public function form(array $form, array &$form_state) { // If there are multiple configurable language types, let the user pick // which one should be applied to this visibility setting. This way users // can limit blocks by interface language or content language for example. - $language_types = language_types_info(); + $info = $this->languageManager->getDefinedLanguageTypesInfo(); $language_type_options = array(); - foreach ($configurable_language_types as $type_key) { - $language_type_options[$type_key] = $language_types[$type_key]['name']; + foreach ($language_types as $type_key) { + $language_type_options[$type_key] = $info[$type_key]['name']; } $form['visibility']['language']['language_type'] = array( '#type' => 'radios', '#title' => $this->t('Language type'), '#options' => $language_type_options, - '#default_value' => !empty($visibility['language']['language_type']) ? $visibility['language']['language_type'] : $configurable_language_types[0], + '#default_value' => !empty($visibility['language']['language_type']) ? $visibility['language']['language_type'] : reset($language_types), '#access' => count($language_type_options) > 1, ); $form['visibility']['language']['langcodes'] = array( diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php index 2e4a4c6..e2bb9da 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php @@ -74,14 +74,13 @@ public function testLanguageBlockVisibility() { $this->drupalPostForm('admin/config/regional/settings', $edit, t('Save configuration')); // Reset the static cache of the language list. - drupal_static_reset('language_list'); - + $this->container->get('language_manager')->reset(); // Check that a page has a block. - $this->drupalget('', array('language' => language_load('en'))); + $this->drupalGet('en'); $this->assertText('Powered by Drupal', 'The body of the custom block appears on the page.'); // Check that a page doesn't has a block for the current language anymore. - $this->drupalGet('', array('language' => language_load('fr'))); + $this->drupalGet('fr'); $this->assertNoText('Powered by Drupal', 'The body of the custom block does not appear on the page.'); } diff --git a/core/modules/block/tests/Drupal/block/Tests/BlockFormControllerTest.php b/core/modules/block/tests/Drupal/block/Tests/BlockFormControllerTest.php index 69b0a1b..f3ae6ef 100644 --- a/core/modules/block/tests/Drupal/block/Tests/BlockFormControllerTest.php +++ b/core/modules/block/tests/Drupal/block/Tests/BlockFormControllerTest.php @@ -66,9 +66,7 @@ public function testGetUniqueMachineName() { ->method('getStorageController') ->will($this->returnValue($block_storage)); - $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager') - ->disableOriginalConstructor() - ->getMock(); + $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $config_factory = $this->getMockBuilder('Drupal\Core\Config\ConfigFactory') ->disableOriginalConstructor() diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php index 03f37e1..54e01b2 100644 --- a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php +++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php @@ -265,7 +265,7 @@ public function getJSSettings(EditorEntity $editor) { // Map the interface language code to a CKEditor translation. $ckeditor_langcodes = $this->getLangcodes(); - $language_interface = $this->languageManager->getLanguage(Language::TYPE_INTERFACE); + $language_interface = $this->languageManager->getCurrentLanguage(); if (isset($ckeditor_langcodes[$language_interface->id])) { $display_langcode = $ckeditor_langcodes[$language_interface->id]; } diff --git a/core/modules/comment/lib/Drupal/comment/CommentFormController.php b/core/modules/comment/lib/Drupal/comment/CommentFormController.php index 3f88b51..0adb4fc 100644 --- a/core/modules/comment/lib/Drupal/comment/CommentFormController.php +++ b/core/modules/comment/lib/Drupal/comment/CommentFormController.php @@ -66,7 +66,7 @@ protected function init(array &$form_state) { // Make the comment inherit the current content language unless specifically // set. if ($comment->isNew()) { - $language_content = \Drupal::languageManager()->getLanguage(Language::TYPE_CONTENT); + $language_content = \Drupal::languageManager()->getCurrentLanguage(Language::TYPE_CONTENT); $comment->langcode->value = $language_content->id; } diff --git a/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php b/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php index 7dd6517..d23ee11 100644 --- a/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php +++ b/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php @@ -16,6 +16,7 @@ use Drupal\Core\Entity\EntityViewBuilderInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Entity\EntityViewBuilder; +use Drupal\Core\Language\LanguageManagerInterface; use Drupal\field\FieldInfo; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -52,6 +53,7 @@ public static function createInstance(ContainerInterface $container, EntityTypeI return new static( $entity_info, $container->get('entity.manager'), + $container->get('language_manager'), $container->get('field.info'), $container->get('module_handler'), $container->get('csrf_token') @@ -65,6 +67,8 @@ public static function createInstance(ContainerInterface $container, EntityTypeI * The entity information array. * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager service. + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager + * The language manager. * @param \Drupal\field\FieldInfo $field_info * The field info service. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler @@ -72,8 +76,8 @@ public static function createInstance(ContainerInterface $container, EntityTypeI * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token * The CSRF token manager service. */ - public function __construct(EntityTypeInterface $entity_info, EntityManagerInterface $entity_manager, FieldInfo $field_info, ModuleHandlerInterface $module_handler, CsrfTokenGenerator $csrf_token) { - parent::__construct($entity_info, $entity_manager); + public function __construct(EntityTypeInterface $entity_info, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, FieldInfo $field_info, ModuleHandlerInterface $module_handler, CsrfTokenGenerator $csrf_token) { + parent::__construct($entity_info, $entity_manager, $language_manager); $this->fieldInfo = $field_info; $this->moduleHandler = $module_handler; $this->csrfToken = $csrf_token; diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php index c76dd41..3e738ce 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php @@ -83,7 +83,6 @@ function setUp() { * Test that comment language is properly set. */ function testCommentLanguage() { - drupal_static_reset('language_list'); // Create two nodes, one for english and one for french, and comment each // node using both english and french as content language by changing URL diff --git a/core/modules/comment/tests/modules/comment_test/comment_test.module b/core/modules/comment/tests/modules/comment_test/comment_test.module index 110e8c5..10f521b 100644 --- a/core/modules/comment/tests/modules/comment_test/comment_test.module +++ b/core/modules/comment/tests/modules/comment_test/comment_test.module @@ -13,7 +13,7 @@ */ function comment_test_entity_info_alter(&$entity_info) { /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */ - if (language_multilingual()) { + if (\Drupal::languageManager()->isMultilingual()) { // Enable language handling for comment fields. $translation = $entity_info['comment']->get('translation'); $translation['comment_test'] = TRUE; diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php index 24edb76..707ed79 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php @@ -35,6 +35,7 @@ public function setUp() { parent::setUp(); config_install_default_config('module', 'config_test'); config_install_default_config('module', 'locale'); + \Drupal::service('config.context')->init(); } /** diff --git a/core/modules/config_translation/lib/Drupal/config_translation/ConfigMapperManager.php b/core/modules/config_translation/lib/Drupal/config_translation/ConfigMapperManager.php index edb431c..4331d6f 100644 --- a/core/modules/config_translation/lib/Drupal/config_translation/ConfigMapperManager.php +++ b/core/modules/config_translation/lib/Drupal/config_translation/ConfigMapperManager.php @@ -13,7 +13,7 @@ use Drupal\Core\Config\Schema\ArrayElement; use Drupal\Core\Config\TypedConfigManager; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Language\LanguageManager; +use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\DefaultPluginManager; use Drupal\Core\Plugin\Discovery\InfoHookDecorator; use Drupal\Core\Plugin\Discovery\YamlDiscovery; @@ -49,14 +49,14 @@ class ConfigMapperManager extends DefaultPluginManager implements ConfigMapperMa * * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend * The cache backend. - * @param \Drupal\Core\Language\LanguageManager $language_manager + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. * @param \Drupal\Core\Config\TypedConfigManager $typed_config_manager * The typed config manager. */ - public function __construct(CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler, TypedConfigManager $typed_config_manager) { + public function __construct(CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, ModuleHandlerInterface $module_handler, TypedConfigManager $typed_config_manager) { $this->typedConfigManager = $typed_config_manager; // Look at all themes and modules. diff --git a/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationController.php b/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationController.php index 3eb0216..03c2a20 100644 --- a/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationController.php +++ b/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationController.php @@ -12,6 +12,7 @@ use Drupal\Core\Controller\ControllerBase; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Language\Language; +use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\PathProcessor\InboundPathProcessorInterface; use Drupal\Core\Session\AccountInterface; use Symfony\Cmf\Component\Routing\RouteObjectInterface; @@ -62,6 +63,13 @@ class ConfigTranslationController extends ControllerBase implements ContainerInj protected $account; /** + * The language manager. + * + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager + */ + protected $languageManager; + + /** * Constructs a ConfigTranslationController. * * @param \Drupal\config_translation\ConfigMapperManagerInterface $config_mapper_manager @@ -74,13 +82,16 @@ class ConfigTranslationController extends ControllerBase implements ContainerInj * The inbound path processor. * @param \Drupal\Core\Session\AccountInterface $account * The current user. + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager + * The language manager. */ - public function __construct(ConfigMapperManagerInterface $config_mapper_manager, AccessManager $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, AccountInterface $account) { + public function __construct(ConfigMapperManagerInterface $config_mapper_manager, AccessManager $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, AccountInterface $account, LanguageManagerInterface $language_manager) { $this->configMapperManager = $config_mapper_manager; $this->accessManager = $access_manager; $this->router = $router; $this->pathProcessor = $path_processor; $this->account = $account; + $this->languageManager = $language_manager; } /** @@ -92,7 +103,8 @@ public static function create(ContainerInterface $container) { $container->get('access_manager'), $container->get('router'), $container->get('path_processor_manager'), - $container->get('current_user') + $container->get('current_user'), + $container->get('language_manager') ); } @@ -119,10 +131,10 @@ public function itemPage(Request $request, $plugin_id) { // not on the system. For example, the configuration shipped in English but // the site has no English configured. Represent the original language in // the table even if it is not currently configured. - $languages = language_list(); + $languages = $this->languageManager->getLanguages(); $original_langcode = $mapper->getLangcode(); if (!isset($languages[$original_langcode])) { - $language_name = language_name($original_langcode); + $language_name = $this->languageManager->getLanguageName($original_langcode); if ($original_langcode == 'en') { $language_name = $this->t('Built-in English'); } diff --git a/core/modules/config_translation/tests/Drupal/config_translation/Tests/ConfigMapperManagerTest.php b/core/modules/config_translation/tests/Drupal/config_translation/Tests/ConfigMapperManagerTest.php index b07e542..5157507 100644 --- a/core/modules/config_translation/tests/Drupal/config_translation/Tests/ConfigMapperManagerTest.php +++ b/core/modules/config_translation/tests/Drupal/config_translation/Tests/ConfigMapperManagerTest.php @@ -47,9 +47,9 @@ public static function getInfo() { public function setUp() { $language = new Language(array('id' => 'en')); - $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager'); + $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $language_manager->expects($this->once()) - ->method('getLanguage') + ->method('getCurrentLanguage') ->with(Language::TYPE_INTERFACE) ->will($this->returnValue($language)); diff --git a/core/modules/content_translation/content_translation.admin.inc b/core/modules/content_translation/content_translation.admin.inc index a39d8f4..f509752 100644 --- a/core/modules/content_translation/content_translation.admin.inc +++ b/core/modules/content_translation/content_translation.admin.inc @@ -275,7 +275,7 @@ function content_translation_form_language_content_settings_validate(array $form } $values = $bundle_settings['settings']['language']; - if (language_is_locked($values['langcode']) && empty($values['language_show'])) { + if (empty($values['language_show']) && \Drupal::languageManager()->isLanguageLocked($values['langcode'])) { foreach (language_list(Language::STATE_LOCKED) as $language) { $locked_languages[] = $language->name; } diff --git a/core/modules/content_translation/content_translation.install b/core/modules/content_translation/content_translation.install index bd817bc..e474d69 100644 --- a/core/modules/content_translation/content_translation.install +++ b/core/modules/content_translation/content_translation.install @@ -6,6 +6,7 @@ */ use Drupal\Core\Language\Language; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl; /** * Implements hook_schema(). @@ -85,8 +86,7 @@ function content_translation_install() { // Assign a fairly low weight to ensure our implementation of // hook_module_implements_alter() is run among the last ones. module_set_weight('content_translation', 10); - language_negotiation_include(); - language_negotiation_set(Language::TYPE_CONTENT, array(LANGUAGE_NEGOTIATION_URL => 0)); + \Drupal::service('language_negotiator')->saveConfiguration(Language::TYPE_CONTENT, array(LanguageNegotiationUrl::METHOD_ID => 0)); } /** diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index 70d20dc..21b315a 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -41,7 +41,7 @@ function content_translation_help($path, $arg) { case 'admin/config/regional/content-language': $output = ''; - if (!language_multilingual()) { + if (!\Drupal::languageManager()->isMultilingual()) { $output .= '
' . t('Before you can translate content, there must be at least two languages added on the languages administration page.', array('!url' => url('admin/config/regional/language'))); } return $output; @@ -331,7 +331,7 @@ function _content_translation_menu_strip_loaders($path) { * The entity whose translation overview should be displayed. */ function content_translation_translate_access(EntityInterface $entity) { - return $entity instanceof ContentEntityInterface && empty($entity->getUntranslated()->language()->locked) && language_multilingual() && $entity->isTranslatable() && + return $entity instanceof ContentEntityInterface && empty($entity->getUntranslated()->language()->locked) && \Drupal::languageManager()->isMultilingual() && $entity->isTranslatable() && (user_access('create content translations') || user_access('update content translations') || user_access('delete content translations')); } @@ -955,7 +955,7 @@ function content_translation_language_configuration_element_process(array $eleme function content_translation_language_configuration_element_validate($element, array &$form_state, array $form) { $key = $form_state['content_translation']['key']; $values = $form_state['values'][$key]; - if (language_is_locked($values['langcode']) && !$values['language_show'] && $values['content_translation']) { + if (!$values['language_show'] && $values['content_translation'] && \Drupal::languageManager()->isLanguageLocked($values['langcode'])) { foreach (language_list(Language::STATE_LOCKED) as $language) { $locked_languages[] = $language->name; } diff --git a/core/modules/content_translation/content_translation.pages.inc b/core/modules/content_translation/content_translation.pages.inc index 43e5331..aa1b1f3 100644 --- a/core/modules/content_translation/content_translation.pages.inc +++ b/core/modules/content_translation/content_translation.pages.inc @@ -33,7 +33,7 @@ function content_translation_overview(EntityInterface $entity) { $header = array(t('Language'), t('Translation'), t('Source language'), t('Status'), t('Operations')); $rows = array(); - if (language_multilingual()) { + if (\Drupal::languageManager()->isMultilingual()) { // If we have a view path defined for the current entity get the switch // links based on it. if (!empty($rel['canonical'])) { @@ -159,12 +159,12 @@ function content_translation_overview(EntityInterface $entity) { * A renderable array of language switch links. */ function _content_translation_get_switch_links($path) { - $links = language_negotiation_get_switch_links(Language::TYPE_CONTENT, $path); + $links = \Drupal::languageManager()->getLanguageSwitchLinks(Language::TYPE_CONTENT, $path); if (empty($links)) { // If content language is set up to fall back to the interface language, // then there will be no switch links for Language::TYPE_CONTENT, ergo we // also need to use interface switch links. - $links = language_negotiation_get_switch_links(Language::TYPE_INTERFACE, $path); + $links = \Drupal::languageManager()->getLanguageSwitchLinks(Language::TYPE_INTERFACE, $path); } return $links; } diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php index 6c6b350..cc3dacc 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php +++ b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php @@ -257,7 +257,7 @@ public function query($use_groupby = FALSE) { $default_langcode = language_default()->id; $langcode = str_replace( array('***CURRENT_LANGUAGE***', '***DEFAULT_LANGUAGE***'), - array($this->languageManager->getLanguage(Language::TYPE_CONTENT), $default_langcode), + array($this->languageManager->getCurrentLanguage(Language::TYPE_CONTENT), $default_langcode), $this->view->display_handler->options['field_langcode'] ); $placeholder = $this->placeholder(); @@ -857,7 +857,7 @@ function field_langcode(EntityInterface $entity) { $default_langcode = language_default()->id; $langcode = str_replace( array('***CURRENT_LANGUAGE***', '***DEFAULT_LANGUAGE***'), - array($this->languageManager->getLanguage(Language::TYPE_CONTENT), $default_langcode), + array($this->languageManager->getCurrentLanguage(Language::TYPE_CONTENT), $default_langcode), $this->view->display_handler->options['field_language'] ); diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module index 4962044..f206e62 100644 --- a/core/modules/filter/filter.module +++ b/core/modules/filter/filter.module @@ -193,7 +193,7 @@ function filter_formats(AccountInterface $account = NULL) { // All available formats are cached for performance. if (!isset($formats['all'])) { - $language_interface = \Drupal::languageManager()->getLanguage(Language::TYPE_INTERFACE); + $language_interface = \Drupal::languageManager()->getCurrentLanguage(); if ($cache = \Drupal::cache()->get("filter_formats:{$language_interface->id}")) { $formats['all'] = $cache->data; } diff --git a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php index 3c0d729..596eb5c 100644 --- a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php +++ b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php @@ -73,6 +73,7 @@ function setUp() { $german = new Language(array( 'id' => 'de', 'name' => 'Deutsch', + 'weight' => -1, )); language_save($german); diff --git a/core/modules/system/config/system.language.types.yml b/core/modules/language/config/language.types.yml similarity index 100% rename from core/modules/system/config/system.language.types.yml rename to core/modules/language/config/language.types.yml diff --git a/core/modules/language/config/schema/language.schema.yml b/core/modules/language/config/schema/language.schema.yml index 1b251c9..9a31be7 100644 --- a/core/modules/language/config/schema/language.schema.yml +++ b/core/modules/language/config/schema/language.schema.yml @@ -1,5 +1,22 @@ # Schema for the configuration files of the Language module. +language.types: + type: mapping + label: 'Language types' + mapping: + all: + type: sequence + label: 'All language types' + sequence: + - type: string + label: 'Language type' + configurable: + type: sequence + label: 'Configurable language types' + sequence: + - type: string + label: 'Language type' + language.detection: type: mapping label: 'Language detection settings' diff --git a/core/modules/language/language.api.php b/core/modules/language/language.api.php index d0a31c4..e93f508 100644 --- a/core/modules/language/language.api.php +++ b/core/modules/language/language.api.php @@ -11,6 +11,73 @@ */ /** + * Define language types. + * + * @return + * An associative array of language type definitions. The keys are the + * identifiers, which are also used as names for global variables representing + * the types in the bootstrap phase. The values are associative arrays that + * may contain the following elements: + * - name: The human-readable language type identifier. + * - description: A description of the language type. + * - locked: A boolean indicating if the user can choose wether to configure + * the language type or not using the UI. + * - fixed: A fixed array of language negotiation method identifiers to use to + * initialize this language. If locked is set to TRUE and fixed is set, it + * will always use the specified methods in the given priority order. If not + * present and locked is TRUE then language-interface will be + * used. + * + * @todo Rename the 'fixed' key to something more meaningful, for instance + * 'negotiation settings'. See https://drupal.org/node/2166879. + * + * @see hook_language_types_info_alter() + * @ingroup language_negotiation + */ +function hook_language_types_info() { + return array( + 'custom_language_type' => array( + 'name' => t('Custom language'), + 'description' => t('A custom language type.'), + 'locked' => FALSE, + ), + 'fixed_custom_language_type' => array( + 'locked' => TRUE, + 'fixed' => array('custom_language_negotiation_method'), + ), + ); +} + +/** + * Perform alterations on language types. + * + * @param $language_types + * Array of language type definitions. + * + * @see hook_language_types_info() + * @ingroup language_negotiation + */ +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.'); + } +} + +/** + * Perform alterations on language negotiation methods. + * + * @param $negotiation_info + * Array of language negotiation method definitions. + * + * @ingroup language_negotiation + */ +function hook_language_negotiation_info_alter(array &$negotiation_info) { + if (isset($negotiation_info['custom_language_method'])) { + $negotiation_info['custom_language_method']['config'] = 'admin/config/regional/language/detection/custom-language-method'; + } +} + +/** * React to a language about to be added or updated in the system. * * @param $language @@ -66,7 +133,7 @@ function hook_language_delete($language) { * @param array $context * A language fallback context. * - * @see \Drupal\Core\Language\LanguageManager::getFallbackCandidates() + * @see \Drupal\Core\Language\LanguageManagerInterface::getFallbackCandidates() */ function hook_language_fallback_candidates_alter(array &$candidates, array $context) { $candidates = array_reverse($candidates); @@ -81,7 +148,7 @@ function hook_language_fallback_candidates_alter(array &$candidates, array $cont * @param array $context * A language fallback context. * - * @see \Drupal\Core\Language\LanguageManager::getFallbackCandidates() + * @see \Drupal\Core\Language\LanguageManagerInterface::getFallbackCandidates() */ function hook_language_fallback_candidates_OPERATION_alter(array &$candidates, array $context) { // We know that the current OPERATION deals with entities so no need to check diff --git a/core/modules/language/language.install b/core/modules/language/language.install index 5103389..3738831 100644 --- a/core/modules/language/language.install +++ b/core/modules/language/language.install @@ -6,6 +6,8 @@ */ use Drupal\Core\Language\Language; +use Drupal\language\ConfigurableLanguageManagerInterface; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl; /** * Implements hook_install(). @@ -14,15 +16,16 @@ * system on multilingual sites without needing any preliminary configuration. */ function language_install() { - // Enable URL language detection for each configurable language type. - require_once DRUPAL_ROOT . '/core/includes/language.inc'; - foreach (language_types_get_configurable() as $type) { - module_load_include('inc', 'language', 'language.negotiation'); - language_negotiation_set($type, array(LANGUAGE_NEGOTIATION_URL => 0)); + $language_manager = \Drupal::languageManager(); + if ($language_manager instanceof ConfigurableLanguageManagerInterface) { + $negotiator = \Drupal::service('language_negotiator'); + $types = $language_manager->getLanguageTypes(); + $negotiator->updateConfiguration($types); + // Enable URL language detection for each configurable language type. + foreach ($types as $type) { + $negotiator->saveConfiguration($type, array(LanguageNegotiationUrl::METHOD_ID => 0)); + } } - - // Update the language count. - language_update_count(); } /** @@ -31,12 +34,9 @@ function language_install() { function language_uninstall() { // Clear variables. variable_del('language_default'); - \Drupal::state()->delete('language_count'); // Clear variables. - variable_del('language_types'); - - foreach (language_types_get_all() as $type) { + foreach (\Drupal::languageManager()->getDefinedLanguageTypes() as $type) { variable_del("language_negotiation_$type"); variable_del("language_negotiation_methods_weight_$type"); } @@ -44,19 +44,4 @@ function language_uninstall() { // Re-initialize the language system so successive calls to t() and other // functions will not expect languages to be present. drupal_language_initialize(); - - // Force the language_count state to be 1, so that when checking if the - // site is multilingual (for example in language_multilingual()), the result - // will be FALSE, because the language module is not installed. - \Drupal::state()->set('language_count', 1); -} - -/** - * Implements hook_requirements(). - */ -function language_requirements($phase) { - if ($phase == 'update') { - // Load the include files to make constants available for updates. - language_negotiation_include(); - } } diff --git a/core/modules/language/language.module b/core/modules/language/language.module index 07a4aad..883ab88 100644 --- a/core/modules/language/language.module +++ b/core/modules/language/language.module @@ -5,8 +5,13 @@ * Add language handling functionality to Drupal. */ -use Drupal\node\NodeTypeInterface; use Drupal\Core\Language\Language; +use Drupal\language\ConfigurableLanguageManager; +use Drupal\language\ConfigurableLanguageManagerInterface; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUI; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrlFallback; +use Drupal\node\NodeTypeInterface; /** * Implements hook_help(). @@ -451,6 +456,7 @@ function language_save($language) { $language_entity->weight = isset($language->weight) ? $language->weight : 0; // Save the record and inform others about the change. + $multilingual = \Drupal::languageManager()->isMultilingual(); $language_entity->save(); $t_args = array('%language' => $language->name, '%langcode' => $language->id); if ($language->is_new) { @@ -467,39 +473,23 @@ function language_save($language) { variable_set('language_default', (array) $language); } - // Kill the static cache in language_list(). - drupal_static_reset('language_list'); - - // Update language count based on unlocked language count. - language_update_count(); - - // Update weight of locked system languages. - language_update_locked_weights(); - - language_negotiation_include(); + $language_manager = \Drupal::languageManager(); + $language_manager->reset(); + if ($language_manager instanceof ConfigurableLanguageManagerInterface) { + $language_manager->updateLockedLanguageWeights(); + } // Update URL Prefixes for all languages after the new default language is // propagated and the language_list() cache is flushed. language_negotiation_url_prefixes_update(); - return $language; -} - -/** - * Updates the language_count state. - * - * This is used to check if a site is multilingual or not. - * - * @see language_multilingual() - */ -function language_update_count() { - $count = 0; - foreach (entity_load_multiple('language_entity') as $language) { - if (!$language->locked) { - $count++; - } + // If after adding this language the site will become multilingual, we need to + // rebuild language services. + if (!$multilingual && $language->is_new) { + ConfigurableLanguageManager::rebuildServices(); } - \Drupal::state()->set('language_count', $count); + + return $language; } /** @@ -521,12 +511,17 @@ function language_delete($langcode) { // Remove the language. entity_delete_multiple('language_entity', array($language->id)); - drupal_static_reset('language_list'); - - language_update_count(); + $language_manager = \Drupal::languageManager(); + $language_manager->reset(); + if ($language_manager instanceof ConfigurableLanguageManagerInterface) { + $language_manager->updateLockedLanguageWeights(); + } - // Update weight of locked system languages. - language_update_locked_weights(); + // If after deleting this language the site will become monolingual, we need + // to rebuild language services. + if (!\Drupal::languageManager()->isMultilingual()) { + ConfigurableLanguageManager::rebuildServices(); + } $t_args = array('%language' => $language->name, '%langcode' => $language->id); watchdog('language', 'The %language (%langcode) language has been removed.', $t_args); @@ -570,8 +565,6 @@ function language_library_info() { * language if none is specified. */ function language_language_types_info() { - language_negotiation_include(); - return array( Language::TYPE_INTERFACE => array( 'name' => t('User interface text'), @@ -581,114 +574,75 @@ function language_language_types_info() { Language::TYPE_CONTENT => array( 'name' => t('Content'), 'description' => t('Order of language detection methods for content. If a version of content is available in the detected language, it will be displayed.'), - 'fixed' => array(LANGUAGE_NEGOTIATION_INTERFACE), + 'fixed' => array(LanguageNegotiationUI::METHOD_ID), 'locked' => TRUE, ), Language::TYPE_URL => array( - 'fixed' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_URL_FALLBACK), + 'fixed' => array(LanguageNegotiationUrl::METHOD_ID, LanguageNegotiationUrlFallback::METHOD_ID), 'locked' => TRUE, ), ); } /** - * Implements hook_language_negotiation_info(). + * Reads language prefixes and uses the langcode if no prefix is set. */ -function language_language_negotiation_info() { - language_negotiation_include(); - $file = drupal_get_path('module', 'language') . '/language.negotiation.inc'; - - $negotiation_info = array(); - $negotiation_info[LANGUAGE_NEGOTIATION_URL] = array( - 'types' => array(Language::TYPE_CONTENT, Language::TYPE_INTERFACE, Language::TYPE_URL), - 'callbacks' => array( - 'negotiation' => 'language_from_url', - 'language_switch' => 'language_switcher_url', - ), - 'file' => $file, - 'weight' => -8, - 'name' => t('URL'), - 'description' => t('Language from the URL (Path prefix or domain).'), - 'config' => 'admin/config/regional/language/detection/url', - ); - - $negotiation_info[LANGUAGE_NEGOTIATION_SESSION] = array( - 'callbacks' => array( - 'negotiation' => 'language_from_session', - 'language_switch' => 'language_switcher_session', - 'url_rewrite' => 'language_url_rewrite_session', - ), - 'file' => $file, - 'weight' => -6, - 'name' => t('Session'), - 'description' => t('Language from a request/session parameter.'), - 'config' => 'admin/config/regional/language/detection/session', - ); - - $negotiation_info[LANGUAGE_NEGOTIATION_USER] = array( - 'callbacks' => array('negotiation' => 'language_from_user'), - 'file' => $file, - 'weight' => -4, - 'name' => t('Account preference for site'), - 'description' => t("The language setting for the site in the user's account."), - ); - - $negotiation_info[LANGUAGE_NEGOTIATION_BROWSER] = array( - 'callbacks' => array('negotiation' => 'language_from_browser'), - 'file' => $file, - 'weight' => -2, - 'cache' => 0, - 'name' => t('Browser'), - 'description' => t("Language from the browser's language settings."), - 'config' => 'admin/config/regional/language/detection/browser', - ); - - $negotiation_info[LANGUAGE_NEGOTIATION_INTERFACE] = array( - 'types' => array(Language::TYPE_CONTENT), - 'callbacks' => array('negotiation' => 'language_from_interface'), - 'file' => $file, - 'weight' => 8, - 'name' => t('Interface'), - 'description' => t('Use the detected interface language.'), - ); +function language_negotiation_url_prefixes() { + return \Drupal::config('language.negotiation')->get('url.prefixes'); +} - $negotiation_info[LANGUAGE_NEGOTIATION_URL_FALLBACK] = array( - 'types' => array(Language::TYPE_URL), - 'callbacks' => array('negotiation' => 'language_url_fallback'), - 'file' => $file, - 'weight' => 8, - 'name' => t('URL fallback'), - 'description' => t('Use an already detected language for URLs if none is found.'), - ); +/** + * Update the list of prefixes from the installed languages. + */ +function language_negotiation_url_prefixes_update() { + $prefixes = language_negotiation_url_prefixes(); + foreach (language_list() as $language) { + // The prefix for this language should be updated if it's not assigned yet + // or the prefix is set to the empty string. + if (empty($prefixes[$language->id])) { + // For the default language, set the prefix to the empty string, + // otherwise use the langcode. + $prefixes[$language->id] = !empty($language->default) ? '' : $language->id; + } + // Otherwise we keep the configured prefix. + } + language_negotiation_url_prefixes_save($prefixes); +} - $negotiation_info[LANGUAGE_NEGOTIATION_USER_ADMIN] = array( - 'types' => array(Language::TYPE_INTERFACE), - 'callbacks' => array('negotiation' => 'language_from_user_admin'), - 'file' => $file, - 'weight' => 10, - 'name' => t('Account preference for administration pages'), - 'description' => t("The language setting for account administration pages in the user's account."), - ); +/** + * Saves language prefix settings. + */ +function language_negotiation_url_prefixes_save(array $prefixes) { + \Drupal::config('language.negotiation') + ->set('url.prefixes', $prefixes) + ->save(); +} - return $negotiation_info; +/** + * Reads language domains. + */ +function language_negotiation_url_domains() { + return \Drupal::config('language.negotiation')->get('url.domains'); } /** - * Include negotiation backend functionality. + * Saves the language domain settings. */ -function language_negotiation_include() { - include_once DRUPAL_ROOT . '/core/includes/language.inc'; - include_once __DIR__ . '/language.negotiation.inc'; +function language_negotiation_url_domains_save(array $domains) { + \Drupal::config('language.negotiation') + ->set('url.domains', $domains) + ->save(); } /** * Implements hook_modules_installed(). */ function language_modules_installed($modules) { - include_once DRUPAL_ROOT . '/core/includes/language.inc'; - // Load configurability options from configuration. - language_types_set(array()); - language_negotiation_purge(); + if (!in_array('language', $modules)) { + $negotiator = \Drupal::service('language_negotiator'); + $negotiator->updateConfiguration(array()); + $negotiator->purgeConfiguration(); + } } /** @@ -706,8 +660,6 @@ function language_language_insert($language) { return; } - language_negotiation_include(); - // Add language to the list of language domains. $domains = language_negotiation_url_domains(); $domains[$language->id] = ''; @@ -718,8 +670,6 @@ function language_language_insert($language) { * Implements hook_language_delete(). */ function language_language_delete($language) { - language_negotiation_include(); - // Remove language from language prefix list. $prefixes = language_negotiation_url_prefixes(); unset($prefixes[$language->id]); @@ -769,29 +719,6 @@ function language_set_browser_drupal_langcode_mappings($mappings) { } /** - * Updates locked system language weights. - */ -function language_update_locked_weights() { - $max_weight = 0; - - // Get maximum weight to update the system languages to keep them on bottom. - foreach (language_list(Language::STATE_CONFIGURABLE) as $language) { - if (!$language->locked && $language->weight > $max_weight) { - $max_weight = $language->weight; - } - } - - // Loop locked languages to maintain the existing order. - foreach (language_list(Language::STATE_LOCKED) as $language) { - $max_weight++; - // Update system languages weight. - \Drupal::config('language.entity.' . $language->id) - ->set('weight', $max_weight) - ->save(); - } -} - -/** * Implements hook_form_FORM_ID_alter for system_regional_settings(). * * @see language_system_regional_settings_form_submit() diff --git a/core/modules/language/language.negotiation.inc b/core/modules/language/language.negotiation.inc deleted file mode 100644 index f87284d..0000000 --- a/core/modules/language/language.negotiation.inc +++ /dev/null @@ -1,529 +0,0 @@ -id; -} - -/** - * Identify language from the Accept-language HTTP header we got. - * - * The algorithm works as follows: - * - map browser language codes to Drupal language codes. - * - order all browser language codes by qvalue from high to low. - * - add generic browser language codes if they aren't already specified - * but with a slightly lower qvalue. - * - find the most specific Drupal language code with the highest qvalue. - * - if 2 or more languages are having the same qvalue, respect the order of - * them inside the $languages array. - * - * We perform browser accept-language parsing only if page cache is disabled, - * otherwise we would cache a user-specific preference. - * - * @param $languages - * An array of language objects for enabled languages ordered by weight. - * - * @return - * A valid language code on success, FALSE otherwise. - */ -function language_from_browser($languages) { - $accept_language = \Drupal::request()->server->get('HTTP_ACCEPT_LANGUAGE'); - if (empty($accept_language)) { - return FALSE; - } - - // The Accept-Language header contains information about the language - // preferences configured in the user's browser / operating system. RFC 2616 - // (section 14.4) defines the Accept-Language header as follows: - // Accept-Language = "Accept-Language" ":" - // 1#( language-range [ ";" "q" "=" qvalue ] ) - // language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" ) - // Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5" - $browser_langcodes = array(); - if (preg_match_all('@(?<=[, ]|^)([a-zA-Z-]+|\*)(?:;q=([0-9.]+))?(?:$|\s*,\s*)@', trim($accept_language), $matches, PREG_SET_ORDER)) { - // Load custom mappings to support browsers that are sending non standard - // language codes. - $mappings = language_get_browser_drupal_langcode_mappings(); - foreach ($matches as $match) { - if ($mappings) { - $langcode = strtolower($match[1]); - foreach ($mappings as $browser_langcode => $drupal_langcode) { - if ($langcode == $browser_langcode) { - $match[1] = $drupal_langcode; - } - } - } - // We can safely use strtolower() here, tags are ASCII. - // RFC2616 mandates that the decimal part is no more than three digits, - // so we multiply the qvalue by 1000 to avoid floating point comparisons. - $langcode = strtolower($match[1]); - $qvalue = isset($match[2]) ? (float) $match[2] : 1; - // Take the highest qvalue for this langcode. Although the request - // supposedly contains unique langcodes, our mapping possibly resolves - // to the same langcode for different qvalues. Keep the highest. - $browser_langcodes[$langcode] = max( - (int) ($qvalue * 1000), - (isset($browser_langcodes[$langcode]) ? $browser_langcodes[$langcode] : 0) - ); - } - } - - // We should take pristine values from the HTTP headers, but Internet Explorer - // from version 7 sends only specific language tags (eg. fr-CA) without the - // corresponding generic tag (fr) unless explicitly configured. In that case, - // we assume that the lowest value of the specific tags is the value of the - // generic language to be as close to the HTTP 1.1 spec as possible. - // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 and - // http://blogs.msdn.com/b/ie/archive/2006/10/17/accept-language-header-for-internet-explorer-7.aspx - asort($browser_langcodes); - foreach ($browser_langcodes as $langcode => $qvalue) { - // For Chinese languages the generic tag is either zh-hans or zh-hant, so we - // need to handle this separately, we can not split $langcode on the - // first occurrence of '-' otherwise we get a non-existing language zh. - // All other languages use a langcode without a '-', so we can safely split - // on the first occurrence of it. - $generic_tag = ''; - if (strlen($langcode) > 7 && (substr($langcode, 0, 7) == 'zh-hant' || substr($langcode, 0, 7) == 'zh-hans')) { - $generic_tag = substr($langcode, 0, 7); - } - else { - $generic_tag = strtok($langcode, '-'); - } - if (!empty($generic_tag) && !isset($browser_langcodes[$generic_tag])) { - // Add the generic langcode, but make sure it has a lower qvalue as the - // more specific one, so the more specific one gets selected if it's - // defined by both the browser and Drupal. - $browser_langcodes[$generic_tag] = $qvalue - 0.1; - } - } - - // Find the enabled language with the greatest qvalue, following the rules of - // RFC 2616 (section 14.4). If several languages have the same qvalue, prefer - // the one with the greatest weight. - $best_match_langcode = FALSE; - $max_qvalue = 0; - foreach ($languages as $langcode => $language) { - // Language tags are case insensitive (RFC2616, sec 3.10). - $langcode = strtolower($langcode); - - // If nothing matches below, the default qvalue is the one of the wildcard - // language, if set, or is 0 (which will never match). - $qvalue = isset($browser_langcodes['*']) ? $browser_langcodes['*'] : 0; - - // Find the longest possible prefix of the browser-supplied language ('the - // language-range') that matches this site language ('the language tag'). - $prefix = $langcode; - do { - if (isset($browser_langcodes[$prefix])) { - $qvalue = $browser_langcodes[$prefix]; - break; - } - } - while ($prefix = substr($prefix, 0, strrpos($prefix, '-'))); - - // Find the best match. - if ($qvalue > $max_qvalue) { - $best_match_langcode = $language->id; - $max_qvalue = $qvalue; - } - } - - return $best_match_langcode; -} - -/** - * Identify language from the user preferences. - * - * @param $languages - * An array of valid language objects. - * - * @return - * A valid language code on success, FALSE otherwise. - */ -function language_from_user($languages) { - // User preference (only for authenticated users). - $user = \Drupal::currentUser(); - - if ($user->id()) { - $langcode = $user->getPreferredLangcode(); - $default_langcode = language_default()->id; - if (!empty($langcode) && $langcode != $default_langcode && isset($languages[$langcode])) { - return $langcode; - } - } - - // No language preference from the user. - return FALSE; -} - -/** - * Identifies admin language from the user preferences. - * - * @param $languages - * An array of valid language objects. - * - * @param \Symfony\Component\HttpFoundation\Request|null $request - * (optional) The HttpRequest object representing the current request. - * Defaults to NULL. - * - * @return - * A valid language code on success, FALSE otherwise. - */ -function language_from_user_admin(array $languages, Request $request = NULL) { - // User preference (only for authenticated users). - $user = \Drupal::currentUser(); - - if ($user->id()) { - $request_path = $request ? urldecode(trim($request->getPathInfo(), '/')) : _current_path(); - $langcode = $user->getPreferredAdminLangcode(); - $default_langcode = language_default()->id; - if (!empty($langcode) && $langcode != $default_langcode && isset($languages[$langcode]) && path_is_admin($request_path)) { - return $langcode; - } - } - - // No language preference from the user or not on an admin path. - return FALSE; -} - -/** - * Identify language from a request/session parameter. - * - * @param $languages - * An array of valid language objects. - * - * @return - * A valid language code on success, FALSE otherwise. - */ -function language_from_session($languages) { - $param = \Drupal::config('language.negotiation')->get('session.parameter'); - $query = \Drupal::request()->query; - - // Request parameter: we need to update the session parameter only if we have - // an authenticated user. - if ($query->has($param) && isset($languages[$langcode = $query->get($param)])) { - $user = \Drupal::currentUser(); - if ($user->id()) { - $_SESSION[$param] = $langcode; - } - return $langcode; - } - - // Session parameter. - if (isset($_SESSION[$param])) { - return $_SESSION[$param]; - } - - return FALSE; -} - -/** - * Identify language via URL prefix or domain. - * - * @param $languages - * An array of valid language objects. - * - * @param \Symfony\Component\HttpFoundation\Request|null $request - * (optional) The HttpRequest object representing the current request. - * Defaults to NULL. - * - * @return - * A valid language code on success, FALSE otherwise. - */ -function language_from_url($languages, Request $request = NULL) { - $language_url = FALSE; - - if (!language_negotiation_method_enabled(LANGUAGE_NEGOTIATION_URL) || !$request) { - return $language_url; - } - - switch (\Drupal::config('language.negotiation')->get('url.source')) { - case LANGUAGE_NEGOTIATION_URL_PREFIX: - - $request_path = urldecode(trim($request->getPathInfo(), '/')); - list($language, $path) = language_url_split_prefix($request_path, $languages); - - if ($language !== FALSE) { - $language_url = $language->id; - } - break; - - case LANGUAGE_NEGOTIATION_URL_DOMAIN: - // Get only the host, not the port. - $http_host= \Drupal::request()->server->get('HTTP_HOST'); - if (strpos($http_host, ':') !== FALSE) { - $http_host_tmp = explode(':', $http_host); - $http_host = current($http_host_tmp); - } - $domains = language_negotiation_url_domains(); - foreach ($languages as $language) { - // Skip the check if the language doesn't have a domain. - if (!empty($domains[$language->id])) { - // Ensure that there is exactly one protocol in the URL when checking - // the hostname. - $host = 'http://' . str_replace(array('http://', 'https://'), '', $domains[$language->id]); - $host = parse_url($host, PHP_URL_HOST); - if ($http_host == $host) { - $language_url = $language->id; - break; - } - } - } - break; - } - - return $language_url; -} - -/** - * 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 negotiation method. 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_negotiation_method_invoke() to every language method callback, - * but it is not actually needed here. Defaults to NULL. - * - * @param $request - * (optional) The HttpRequest object representing the current request. - * - * @param $language_type - * (optional) The language type to fall back to. Defaults to the interface - * language. - * - * @return - * A valid language code. - */ -function language_url_fallback($language = NULL, $request = NULL, $language_type = Language::TYPE_INTERFACE) { - $default = language_default(); - $prefix = (\Drupal::config('language.negotiation')->get('url.source') == 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. - $domains = language_negotiation_url_domains(); - $prefixes = language_negotiation_url_prefixes(); - if (($prefix && empty($prefixes[$default->id])) || (!$prefix && empty($domains[$default->id]))) { - return $default->id; - } - else { - $langcode = language($language_type)->id; - return $langcode; - } -} - -/** - * Return links for the URL language switcher block. - * - * Translation links may be provided by other modules. - */ -function language_switcher_url($type, $path) { - $languages = language_list(); - $links = array(); - - foreach ($languages as $language) { - $links[$language->id] = array( - 'href' => $path, - 'title' => $language->name, - 'language' => $language, - 'attributes' => array('class' => array('language-link')), - ); - } - - return $links; -} - -/** - * Return the session language switcher block. - */ -function language_switcher_session($type, $path) { - $param = \Drupal::config('language.negotiation')->get('session.parameter'); - $language_query = isset($_SESSION[$param]) ? $_SESSION[$param] : language($type)->id; - - $languages = language_list(); - $links = array(); - - $query = \Drupal::request()->query->all(); - - foreach ($languages as $language) { - $langcode = $language->id; - $links[$langcode] = array( - 'href' => $path, - 'title' => $language->name, - 'attributes' => array('class' => array('language-link')), - 'query' => $query, - ); - if ($language_query != $langcode) { - $links[$langcode]['query'][$param] = $langcode; - } - else { - $links[$langcode]['attributes']['class'][] = ' session-active'; - } - } - - return $links; -} - -/** - * Reads language prefixes and uses the langcode if no prefix is set. - */ -function language_negotiation_url_prefixes() { - return \Drupal::config('language.negotiation')->get('url.prefixes'); -} - -/** - * Update the list of prefixes from the installed languages. - */ -function language_negotiation_url_prefixes_update() { - $prefixes = language_negotiation_url_prefixes(); - foreach (language_list() as $language) { - // The prefix for this language should be updated if it's not assigned yet - // or the prefix is set to the empty string. - if (empty($prefixes[$language->id])) { - // For the default language, set the prefix to the empty string, - // otherwise use the langcode. - $prefixes[$language->id] = !empty($language->default) ? '' : $language->id; - } - // Otherwise we keep the configured prefix. - } - language_negotiation_url_prefixes_save($prefixes); -} - -/** - * Saves language prefix settings. - */ -function language_negotiation_url_prefixes_save(array $prefixes) { - \Drupal::config('language.negotiation') - ->set('url.prefixes', $prefixes) - ->save(); -} - -/** - * Reads language domains. - */ -function language_negotiation_url_domains() { - return \Drupal::config('language.negotiation')->get('url.domains'); -} - -/** - * Saves the language domain settings. - */ -function language_negotiation_url_domains_save(array $domains) { - \Drupal::config('language.negotiation') - ->set('url.domains', $domains) - ->save(); -} - -/** - * Rewrite URLs for the Session language negotiation method. - */ -function language_url_rewrite_session(&$path, &$options) { - static $query_rewrite, $query_param, $query_value; - - // The following values are not supposed to change during a single page - // request processing. - if (!isset($query_rewrite)) { - $user = \Drupal::currentUser(); - if (!$user->id()) { - $languages = language_list(); - $query_param = String::checkPlain(\Drupal::config('language.negotiation')->get('session.parameter')); - $query = \Drupal::request()->query; - if ($query->has($query_param)) { - $query_value = String::checkPlain(\Drupal::request()->query->get($query_param)); - } - else { - return FALSE; - } - $query_rewrite = isset($languages[$query_value]) && language_negotiation_method_enabled(LANGUAGE_NEGOTIATION_SESSION); - } - else { - $query_rewrite = FALSE; - } - } - - // If the user is anonymous, the user language negotiation method is enabled, - // and the corresponding option has been set, we must preserve any explicit - // user language preference even with cookies disabled. - if ($query_rewrite) { - if (is_string($options['query'])) { - $query = array(); - parse_str($options['query'], $query); - $options['query'] = $query; - } - if (!isset($options['query'][$query_param])) { - $options['query'][$query_param] = $query_value; - } - } -} diff --git a/core/modules/language/language.services.yml b/core/modules/language/language.services.yml index d6599b3..ee06f7a 100644 --- a/core/modules/language/language.services.yml +++ b/core/modules/language/language.services.yml @@ -1,7 +1,10 @@ services: - path_processor_language: - class: Drupal\language\HttpKernel\PathProcessorLanguage - arguments: ['@config.factory', '@settings', '@language_manager'] - tags: - - { name: path_processor_inbound, priority: 300 } - - { name: path_processor_outbound, priority: 100 } + plugin.manager.language_negotiation_method: + class: Drupal\language\LanguageNegotiationMethodManager + arguments: ['@container.namespaces', '@cache.cache', '@module_handler'] + language_negotiator: + class: Drupal\language\LanguageNegotiator + arguments: ['@language_manager', '@plugin.manager.language_negotiation_method', '@config.factory', '@settings'] + calls: + - [initLanguageManager] + diff --git a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php new file mode 100644 index 0000000..a570700 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php @@ -0,0 +1,400 @@ +deleteAll(); + } + + /** + * Constructs a new ConfigurableLanguageManager object. + * + * @param \Drupal\Core\Config\ConfigFactory $config_factory + * The configuration storage service. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler service. + */ + public function __construct(ConfigFactory $config_factory, ModuleHandlerInterface $module_handler) { + $this->configFactory = $config_factory; + $this->moduleHandler = $module_handler; + } + + /** + * Initializes per-language overrides for configuration. + * + * @todo Remove this and just set the current language onto the config + * factory. See https://drupal.org/node/2098119. + */ + public function initConfigOverrides() { + $service_id = 'locale_config_subscriber'; + if (\Drupal::getContainer()->has($service_id)) { + $subscriber = \Drupal::service($service_id); + \Drupal::service('event_dispatcher')->addSubscriberService($service_id, get_class($subscriber)); + } + } + + /** + * {@inheritdoc} + */ + public function init() { + if (!$this->initialized) { + foreach ($this->getDefinedLanguageTypes() as $type) { + $this->getCurrentLanguage($type); + } + $this->initialized = TRUE; + } + } + + /** + * {@inheritdoc} + */ + public function isMultilingual() { + return count($this->getLanguages(Language::STATE_CONFIGURABLE)) > 1; + } + + /** + * {@inheritdoc} + */ + public function getLanguageTypes() { + $this->loadLanguageTypesConfiguration(); + return $this->languageTypes['configurable']; + } + + /** + * {@inheritdoc} + */ + public function getDefinedLanguageTypes() { + $this->loadLanguageTypesConfiguration(); + return $this->languageTypes['all']; + } + + /** + * Retrieves language types from the configuration storage. + * + * @return array + * An array of language type names. + */ + protected function loadLanguageTypesConfiguration() { + if (!$this->languageTypes) { + $this->languageTypes = $this->configFactory->get('language.types')->get() ?: array('configurable' => array(), 'all' => parent::getLanguageTypes()); + } + return $this->languageTypes; + } + + /** + * {@inheritdoc} + */ + public function getDefinedLanguageTypesInfo() { + if (!isset($this->languageTypesInfo)) { + $info = $this->moduleHandler->invokeAll('language_types_info'); + // Let other modules alter the list of language types. + $this->moduleHandler->alter('language_types_info', $info); + $this->languageTypesInfo = $info; + } + return $this->languageTypesInfo; + } + + /** + * {@inheritdoc} + */ + function disableLanguageTypes(array $types) { + $this->languageTypes['configurable'] = array_diff($this->getLanguageTypes(), $types); + $this->saveLanguageTypesConfiguration($this->languageTypes); + } + + /** + * Stores language types configuration. + */ + public function saveLanguageTypesConfiguration(array $config) { + $this->configFactory->get('language.types')->setData($config)->save(); + } + + /** + * {@inheritdoc} + */ + public function getCurrentLanguage($type = Language::TYPE_INTERFACE) { + if (!isset($this->negotiatedLanguages[$type])) { + // Ensure we have a valid value for this language type. + $this->negotiatedLanguages[$type] = $this->getDefaultLanguage(); + + if ($this->negotiator && $this->isMultilingual()) { + if (!$this->initializing) { + $this->initializing = TRUE; + $this->negotiatedLanguages[$type] = $this->negotiator->initializeType($type); + $this->initializing = FALSE; + } + // If the current interface language needs to be retrieved during + // initialization we return the system language. This way string + // translation calls happening during initialization will return the + // original strings which can be translated by calling them again + // afterwards. This can happen for instance while parsing negotiation + // method definitions. + elseif ($type == Language::TYPE_INTERFACE) { + return new Language(array('id' => Language::LANGCODE_SYSTEM)); + } + } + } + + return $this->negotiatedLanguages[$type]; + } + + /** + * {@inheritdoc} + */ + public function reset($type = NULL) { + if (!isset($type)) { + $this->initialized = FALSE; + $this->negotiatedLanguages = array(); + $this->languageTypes = NULL; + $this->languageTypesInfo = NULL; + $this->languages = NULL; + $this->defaultLanguage = NULL; + if ($this->negotiator) { + $this->negotiator->reset(); + } + } + elseif (isset($this->negotiatedLanguages[$type])) { + unset($this->negotiatedLanguages[$type]); + } + } + + /** + * {@inheritdoc} + */ + public function setRequest(Request $request) { + $this->request = $request; + } + + /** + * {@inheritdoc} + */ + public function getNegotiator() { + return $this->negotiator; + } + + /** + * {@inheritdoc} + */ + public function setNegotiator(LanguageNegotiatorInterface $negotiator) { + $this->negotiator = $negotiator; + $this->reset(); + } + + /** + * {@inheritdoc} + */ + public function getDefaultLanguage() { + if (!isset($this->defaultLanguage)) { + // @todo Convert to CMI https://drupal.org/node/1827038 and + // https://drupal.org/node/2108599. + $default_info = variable_get('language_default', Language::$defaultValues); + $this->defaultLanguage = new Language($default_info + array('default' => TRUE)); + } + return $this->defaultLanguage; + } + + /** + * {@inheritdoc} + */ + public function getLanguages($flags = Language::STATE_CONFIGURABLE) { + if (!isset($this->languages)) { + // Prepopulate the language list with the default language to keep things + // working even if we have no configuration. + $default = $this->getDefaultLanguage(); + $this->languages = array($default->id => $default); + + // Retrieve the config storage to list available languages. + $prefix = 'language.entity.'; + $storage = $this->configFactory->get($prefix . Language::LANGCODE_NOT_SPECIFIED)->getStorage(); + $config_ids = $storage->listAll($prefix); + + // Instantiate languages from config objects. + $weight = 0; + foreach ($this->configFactory->loadMultiple($config_ids) as $config) { + $data = $config->get(); + $langcode = $data['id']; + // Initialize default property so callers have an easy reference and can + // save the same object without data loss. + $data['default'] = ($langcode == $default->id); + $data['name'] = $data['label']; + $this->languages[$langcode] = new Language($data); + $weight = max(array($weight, $this->languages[$langcode]->weight)); + } + + // Add locked languages, they will be filtered later if needed. + $this->languages += $this->getDefaultLockedLanguages($weight); + + // Sort the language list by weight. + Language::sort($this->languages); + } + + return parent::getLanguages($flags); + } + + /** + * {@inheritdoc} + */ + public function updateLockedLanguageWeights() { + $max_weight = 0; + + // Get maximum weight to update the system languages to keep them on bottom. + foreach ($this->getLanguages(Language::STATE_CONFIGURABLE) as $language) { + if (!$language->locked && $language->weight > $max_weight) { + $max_weight = $language->weight; + } + } + + // Loop locked languages to maintain the existing order. + $locked_languages = $this->getLanguages(Language::STATE_LOCKED); + $config_ids = array_map(function($language) { return 'language.entity.' . $language->id; }, $locked_languages); + foreach ($this->configFactory->loadMultiple($config_ids) as $config_id => $config) { + // Update system languages weight. + $max_weight++; + $config->set('weight', $max_weight); + $config->save(); + } + } + + /** + * {@inheritdoc} + */ + public function getFallbackCandidates($langcode = NULL, array $context = array()) { + if ($this->isMultilingual()) { + // Get languages ordered by weight, add Language::LANGCODE_NOT_SPECIFIED + // at the end. + $candidates = array_keys($this->getLanguages()); + $candidates[] = Language::LANGCODE_NOT_SPECIFIED; + $candidates = MapArray::copyValuesToKeys($candidates); + + // The first candidate should always be the desired language if specified. + if (!empty($langcode)) { + $candidates = array($langcode => $langcode) + $candidates; + } + + // Let other modules hook in and add/change candidates. + $type = 'language_fallback_candidates'; + $types = array(); + if (!empty($context['operation'])) { + $types[] = $type . '_' . $context['operation']; + } + $types[] = $type; + $this->moduleHandler->alter($types, $candidates, $context); + } + else { + $candidates = parent::getFallbackCandidates($langcode, $context); + } + + return $candidates; + } + + /** + * {@inheritdoc} + */ + public function getLanguageSwitchLinks($type, $path) { + $links = FALSE; + + if ($this->negotiator) { + foreach ($this->negotiator->getNegotiationMethods($type) as $method_id => $method) { + $reflector = new \ReflectionClass($method['class']); + + if ($reflector->implementsInterface('\Drupal\language\LanguageSwitcherInterface')) { + $result = $this->negotiator->getNegotiationMethodInstance($method_id)->getLanguageSwitchLinks($this->request, $type, $path); + + if (!empty($result)) { + // Allow modules to provide translations for specific links. + $this->moduleHandler->alter('language_switch_links', $result, $type, $path); + $links = (object) array('links' => $result, 'method_id' => $method_id); + break; + } + } + } + } + + return $links; + } + +} diff --git a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php new file mode 100644 index 0000000..6606a91 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php @@ -0,0 +1,89 @@ +languageManager = $language_manager; + $this->negotiator = $negotiator; $this->translation = $translation; + $this->currentUser = $current_user; } /** @@ -56,10 +77,13 @@ public function __construct(LanguageManager $language_manager, TranslatorInterfa */ public function onKernelRequestLanguage(GetResponseEvent $event) { if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) { - $this->languageManager->setRequest($event->getRequest()); + $request = $event->getRequest(); + $this->negotiator->setContext($this->currentUser, $request); + $this->languageManager->setNegotiator($this->negotiator); + $this->languageManager->setRequest($request); // After the language manager has initialized, set the default langcode // for the string translations. - $langcode = $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id; + $langcode = $this->languageManager->getCurrentLanguage()->id; $this->translation->setDefaultLangcode($langcode); } } diff --git a/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php b/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php index bca5523..aa65f91 100644 --- a/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php +++ b/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php @@ -98,6 +98,7 @@ public function submitForm(array &$form, array &$form_state) { } // Save the language and inform the user that it happened. $language = language_save($language); + drupal_set_message($this->t('The language %language has been created and can now be used.', array('%language' => $language->name))); // Tell the user they have the option to add a language switcher block diff --git a/core/modules/language/lib/Drupal/language/Form/LanguageEditForm.php b/core/modules/language/lib/Drupal/language/Form/LanguageEditForm.php index 66e1ed1..2dacdc2 100644 --- a/core/modules/language/lib/Drupal/language/Form/LanguageEditForm.php +++ b/core/modules/language/lib/Drupal/language/Form/LanguageEditForm.php @@ -54,6 +54,7 @@ public function submitForm(array &$form, array &$form_state) { $language->name = $form_state['values']['name']; $language->direction = $form_state['values']['direction']; language_save($language); + $form_state['redirect_route']['route_name'] = 'language.admin_overview'; } diff --git a/core/modules/language/lib/Drupal/language/Form/NegotiationConfigureForm.php b/core/modules/language/lib/Drupal/language/Form/NegotiationConfigureForm.php index 01dec5b..6e0e266 100644 --- a/core/modules/language/lib/Drupal/language/Form/NegotiationConfigureForm.php +++ b/core/modules/language/lib/Drupal/language/Form/NegotiationConfigureForm.php @@ -14,6 +14,9 @@ use Drupal\Core\Config\ConfigFactory; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormBase; +use Drupal\language\ConfigurableLanguageManagerInterface; +use Drupal\language\LanguageNegotiatorInterface; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSelected; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -22,36 +25,49 @@ class NegotiationConfigureForm extends FormBase { /** - * Stores the configuration object for system.language.types. + * Stores the configuration object for language.types. * * @var \Drupal\Core\Config\Config */ - protected $languageTypesConfig; + protected $languageTypes; /** - * The block manager. + * The language manager. * - * @var \Drupal\block\Plugin\Type\BlockManager + * @var \Drupal\language\ConfigurableLanguageManagerInterface */ - protected $blockManager; + protected $languageManager; /** - * The module handler. + * The language negotiator. * - * @var \Drupal\Core\Extension\ModuleHandlerInterface + * @var \Drupal\language\LanguageNegotiatorInterface */ - protected $moduleHandler; + protected $negotiator; + + /** + * The block manager. + * + * @var \Drupal\block\Plugin\Type\BlockManager + */ + protected $blockManager; /** * Constructs a NegotiationConfigureForm object. * * @param \Drupal\Core\Config\ConfigFactory $config_factory * The factory for configuration objects. + * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager + * The language manager. + * @param \Drupal\language\LanguageNegotiatorInterface $negotiator + * The language negotiation methods manager. * @param \Drupal\block\Plugin\Type\BlockManager $block_manager * The block manager, or NULL if not available. */ - public function __construct(ConfigFactory $config_factory, BlockManager $block_manager = NULL) { - $this->languageTypesConfig = $config_factory->get('system.language.types'); + public function __construct(ConfigFactory $config_factory, ConfigurableLanguageManagerInterface $language_manager, LanguageNegotiatorInterface $negotiator, BlockManager $block_manager = NULL) { + $this->languageTypes = $config_factory->get('language.types'); + $this->languageManager = $language_manager; + $this->negotiator = $negotiator; $this->blockManager = $block_manager; } @@ -61,6 +77,8 @@ public function __construct(ConfigFactory $config_factory, BlockManager $block_m public static function create(ContainerInterface $container) { return new static( $container->get('config.factory'), + $container->get('language_manager'), + $container->get('language_negotiator'), $container->has('plugin.manager.block') ? $container->get('plugin.manager.block') : NULL ); } @@ -76,14 +94,12 @@ public function getFormID() { * {@inheritdoc} */ public function buildForm(array $form, array &$form_state) { - language_negotiation_include(); - - $configurable = $this->languageTypesConfig->get('configurable'); + $configurable = $this->languageTypes->get('configurable'); $form = array( '#theme' => 'language_negotiation_configure_form', - '#language_types_info' => language_types_info(), - '#language_negotiation_info' => language_negotiation_info(), + '#language_types_info' => $this->languageManager->getDefinedLanguageTypesInfo(), + '#language_negotiation_info' => $this->negotiator->getNegotiationMethods(), ); $form['#language_types'] = array(); @@ -111,10 +127,9 @@ public function buildForm(array $form, array &$form_state) { * {@inheritdoc} */ public function submitForm(array &$form, array &$form_state) { - language_negotiation_include(); $configurable_types = $form['#language_types']; - $stored_values = $this->languageTypesConfig->get('configurable'); + $stored_values = $this->languageTypes->get('configurable'); $customized = array(); $method_weights_type = array(); @@ -122,7 +137,7 @@ public function submitForm(array &$form, array &$form_state) { $customized[$type] = in_array($type, $stored_values); $method_weights = array(); $enabled_methods = $form_state['values'][$type]['enabled']; - $enabled_methods[LANGUAGE_NEGOTIATION_SELECTED] = TRUE; + $enabled_methods[LanguageNegotiationSelected::METHOD_ID] = TRUE; $method_weights_input = $form_state['values'][$type]['weight']; if (isset($form_state['values'][$type]['configurable'])) { $customized[$type] = !empty($form_state['values'][$type]['configurable']); @@ -141,11 +156,11 @@ public function submitForm(array &$form, array &$form_state) { // Update non-configurable language types and the related language // negotiation configuration. - language_types_set(array_keys(array_filter($customized))); + $this->negotiator->updateConfiguration(array_keys(array_filter($customized))); // Update the language negotiations after setting the configurability. foreach ($method_weights_type as $type => $method_weights) { - language_negotiation_set($type, $method_weights); + $this->negotiator->saveConfiguration($type, $method_weights); } // Clear block definitions cache since the available blocks and their names @@ -183,7 +198,7 @@ protected function configureFormTable(array &$form, $type) { ); // Only show configurability checkbox for the unlocked language types. if (empty($info['locked'])) { - $configurable = $this->languageTypesConfig->get('configurable'); + $configurable = $this->languageTypes->get('configurable'); $table_form['configurable'] = array( '#type' => 'checkbox', '#title' => $this->t('Customize %language_name language detection to differ from User interface text language detection settings.', array('%language_name' => $info['name'])), @@ -246,7 +261,7 @@ protected function configureFormTable(array &$form, $type) { '#title_display' => 'invisible', '#default_value' => $enabled, ); - if ($method_id === LANGUAGE_NEGOTIATION_SELECTED) { + if ($method_id === LanguageNegotiationSelected::METHOD_ID) { $table_form['enabled'][$method_id]['#default_value'] = TRUE; $table_form['enabled'][$method_id]['#attributes'] = array('disabled' => 'disabled'); } diff --git a/core/modules/language/lib/Drupal/language/Form/NegotiationUrlForm.php b/core/modules/language/lib/Drupal/language/Form/NegotiationUrlForm.php index ac709e3..172e6b0 100644 --- a/core/modules/language/lib/Drupal/language/Form/NegotiationUrlForm.php +++ b/core/modules/language/lib/Drupal/language/Form/NegotiationUrlForm.php @@ -8,6 +8,7 @@ namespace Drupal\language\Form; use Drupal\Core\Form\ConfigFormBase; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl; /** * Configure the URL language negotiation method for this site. @@ -27,14 +28,13 @@ public function getFormId() { public function buildForm(array $form, array &$form_state) { global $base_url; $config = $this->configFactory->get('language.negotiation'); - 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'), + LanguageNegotiationUrl::CONFIG_PATH_PREFIX => t('Path prefix'), + LanguageNegotiationUrl::CONFIG_DOMAIN => t('Domain'), ), '#default_value' => $config->get('url.source'), ); @@ -47,7 +47,7 @@ public function buildForm(array $form, array &$form_state) { '#states' => array( 'visible' => array( ':input[name="language_negotiation_url_part"]' => array( - 'value' => (string) LANGUAGE_NEGOTIATION_URL_PREFIX, + 'value' => (string) LanguageNegotiationUrl::CONFIG_PATH_PREFIX, ), ), ), @@ -60,7 +60,7 @@ public function buildForm(array $form, array &$form_state) { '#states' => array( 'visible' => array( ':input[name="language_negotiation_url_part"]' => array( - 'value' => (string) LANGUAGE_NEGOTIATION_URL_DOMAIN, + 'value' => (string) LanguageNegotiationUrl::CONFIG_DOMAIN, ), ), ), @@ -103,7 +103,7 @@ public function validateForm(array &$form, array &$form_state) { $value = $form_state['values']['prefix'][$langcode]; if ($value === '') { - if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LANGUAGE_NEGOTIATION_URL_PREFIX) { + if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX) { // Throw a form error if the prefix is blank for a non-default language, // although it is required for selected negotiation type. $this->setFormError("prefix][$langcode", $form_state, t('The prefix may only be left blank for the default language.')); @@ -127,7 +127,7 @@ public function validateForm(array &$form, array &$form_state) { $value = $form_state['values']['domain'][$langcode]; if ($value === '') { - if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LANGUAGE_NEGOTIATION_URL_DOMAIN) { + if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LanguageNegotiationUrl::CONFIG_DOMAIN) { // Throw a form error if the domain is blank for a non-default language, // although it is required for selected negotiation type. $this->setFormError("domain][$langcode", $form_state, t('The domain may only be left blank for the default language.')); diff --git a/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php b/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php index d212c7b..0a9137a 100644 --- a/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php +++ b/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php @@ -8,13 +8,14 @@ namespace Drupal\language\HttpKernel; use Drupal\Component\Utility\Settings; +use Drupal\Component\Utility\Unicode; use Drupal\Core\Config\ConfigFactory; -use Drupal\Core\Language\Language; -use Drupal\Core\Language\LanguageManager; use Drupal\Core\PathProcessor\InboundPathProcessorInterface; use Drupal\Core\PathProcessor\OutboundPathProcessorInterface; -use Symfony\Component\HttpKernel\HttpKernelInterface; +use Drupal\language\ConfigurableLanguageManagerInterface; +use Drupal\language\LanguageNegotiatorInterface; use Symfony\Component\HttpFoundation\Request; +use Drupal\Core\Session\AccountInterface; /** * Processes the inbound path using path alias lookups. @@ -38,129 +39,121 @@ class PathProcessorLanguage implements InboundPathProcessorInterface, OutboundPa /** * Language manager for retrieving the url language type. * - * @var \Drupal\Core\Language\LanguageManager + * @var \Drupal\language\ConfigurableLanguageManagerInterface */ protected $languageManager; /** - * An array of enabled languages. + * The language negotiator. + * + * @var \Drupal\language\LanguageNegotiatorInterface + */ + protected $negotiator; + + /** + * The current active user. + * + * @return \Drupal\Core\Session\AccountInterface + */ + protected $currentUser; + + /** + * Local cache for language path processors. * * @var array */ - protected $languages; + protected $processors; + /** + * Flag indicating whether the site is multilingual. + * + * @var bool + */ + protected $multilingual; /** * Constructs a PathProcessorLanguage object. * * @param \Drupal\Core\Config\ConfigFactory $config * A config factory object for retrieving configuration settings. - * - * @param array $languages - * An array of languages, keyed by language code, representing the languages - * currently enabled on the site. + * @param \Drupal\Component\Utility\Settings $settings + * The settings instance. + * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager + * The configurable language manager. + * @param \Drupal\language\LanguageNegotiatorInterface + * The language negotiator. + * @param \Drupal\Core\Session\AccountInterface $current_user + * The current active user. */ - public function __construct(ConfigFactory $config, Settings $settings, LanguageManager $language_manager, array $languages = array()) { + public function __construct(ConfigFactory $config, Settings $settings, ConfigurableLanguageManagerInterface $language_manager, LanguageNegotiatorInterface $negotiator, AccountInterface $current_user) { $this->config = $config; $this->mixedModeSessions = $settings->get('mixed_mode_sessions', FALSE); $this->languageManager = $language_manager; - if (empty($languages)) { - $languages = language_list(); - } - $this->languages = $languages; + $this->negotiator = $negotiator; + $this->currentUser = $current_user; } /** - * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processInbound(). + * {@inheritdoc} */ public function processInbound($path, Request $request) { if (!empty($path)) { - $args = explode('/', $path); - $prefix = array_shift($args); - - // Search prefix within enabled languages. - $prefixes = $this->config->get('language.negotiation')->get('url.prefixes'); - foreach ($this->languages as $language) { - if (isset($prefixes[$language->id]) && $prefixes[$language->id] == $prefix) { - // Rebuild $path with the language removed. - return implode('/', $args); - } + $scope = 'inbound'; + if (!isset($this->processors[$scope])) { + $this->initProcessors($scope); + } + foreach ($this->processors[$scope] as $instance) { + $path = $instance->processInbound($path, $request); } } return $path; } /** - * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processOutbound(). + * {@inheritdoc} */ public function processOutbound($path, &$options = array(), Request $request = NULL) { - if (!$this->languageManager->isMultilingual()) { - return $path; - } - $url_scheme = 'http'; - $port = 80; - if ($request) { - $url_scheme = $request->getScheme(); - $port = $request->getPort(); + if (!isset($this->multilingual)) { + $this->multilingual = $this->languageManager->isMultilingual(); } - $languages = array_flip(array_keys($this->languages)); - // Language can be passed as an option, or we go for current URL language. - if (!isset($options['language'])) { - $language_url = $this->languageManager->getLanguage(Language::TYPE_URL); - $options['language'] = $language_url; - } - // We allow only enabled languages here. - elseif (is_object($options['language']) && !isset($languages[$options['language']->id])) { - return $path; - } - $url_source = $this->config->get('language.negotiation')->get('url.source'); - // @todo Go back to using a constant instead of the string 'path_prefix' once we can use a class - // constant. - if ($url_source == 'path_prefix') { - $prefixes = $this->config->get('language.negotiation')->get('url.prefixes'); - if (is_object($options['language']) && !empty($prefixes[$options['language']->id])) { - return empty($path) ? $prefixes[$options['language']->id] : $prefixes[$options['language']->id] . '/' . $path; + if ($this->multilingual) { + $this->negotiator->setContext($this->currentUser, $request); + $scope = 'outbound'; + if (!isset($this->processors[$scope])) { + $this->initProcessors($scope); + } + // Execute outbound language processors. + $options['mixed_mode_sessions'] = $this->mixedModeSessions; + foreach ($this->processors[$scope] as $instance) { + $path = $instance->processOutbound($path, $options, $request); + } + // No language dependent path allowed in this mode. + if (empty($this->processors[$scope])) { + unset($options['language']); } } - elseif ($url_source == 'domain') { - $domains = $this->config->get('language.negotiation')->get('url.domains'); - if (is_object($options['language']) && !empty($domains[$options['language']->id])) { - - // Save the original base URL. If it contains a port, we need to - // retain it below. - if (!empty($options['base_url'])) { - // The colon in the URL scheme messes up the port checking below. - $normalized_base_url = str_replace(array('https://', 'http://'), '', $options['base_url']); - } - - // Ask for an absolute URL with our modified base URL. - $options['absolute'] = TRUE; - $options['base_url'] = $url_scheme . '://' . $domains[$options['language']->id]; - - // In case either the original base URL or the HTTP host contains a - // port, retain it. - if (isset($normalized_base_url) && strpos($normalized_base_url, ':') !== FALSE) { - list( , $port) = explode(':', $normalized_base_url); - $options['base_url'] .= ':' . $port; - } - elseif ($port != 80) { - $options['base_url'] .= ':' . $port; - } + return $path; + } - if (isset($options['https']) && $this->mixedModeSessions) { - if ($options['https'] === TRUE) { - $options['base_url'] = str_replace('http://', 'https://', $options['base_url']); - } - elseif ($options['https'] === FALSE) { - $options['base_url'] = str_replace('https://', 'http://', $options['base_url']); + /** + * Initializes the local cache for language path processors. + * + * @param string $scope + * The scope of the processors: "inbound" or "outbound". + */ + protected function initProcessors($scope) { + $interface = '\Drupal\Core\PathProcessor\\' . Unicode::ucfirst($scope) . 'PathProcessorInterface'; + $this->processors[$scope] = array(); + foreach ($this->languageManager->getLanguageTypes() as $type) { + foreach ($this->negotiator->getNegotiationMethods($type) as $method_id => $method) { + if (!isset($this->processors[$scope][$method_id])) { + $reflector = new \ReflectionClass($method['class']); + if ($reflector->implementsInterface($interface)) { + $this->processors[$scope][$method_id] = $this->negotiator->getNegotiationMethodInstance($method_id); } } - - // Add Drupal's subfolder from the base_path if there is one. - $options['base_url'] .= rtrim(base_path(), '/'); } } - return $path; } } diff --git a/core/modules/language/lib/Drupal/language/LanguageListController.php b/core/modules/language/lib/Drupal/language/LanguageListController.php index 9d65146..601d059 100644 --- a/core/modules/language/lib/Drupal/language/LanguageListController.php +++ b/core/modules/language/lib/Drupal/language/LanguageListController.php @@ -95,11 +95,11 @@ public function buildForm(array $form, array &$form_state) { public function submitForm(array &$form, array &$form_state) { parent::submitForm($form, $form_state); - // Kill the static cache in language_list(). - drupal_static_reset('language_list'); - - // Update weight of locked system languages. - language_update_locked_weights(); + $language_manager = \Drupal::languageManager(); + $language_manager->reset(); + if ($language_manager instanceof ConfigurableLanguageManagerInterface) { + $language_manager->updateLockedLanguageWeights(); + } drupal_set_message(t('Configuration saved.')); } diff --git a/core/modules/language/lib/Drupal/language/LanguageNegotiationMethodBase.php b/core/modules/language/lib/Drupal/language/LanguageNegotiationMethodBase.php new file mode 100644 index 0000000..2f2b5cf --- /dev/null +++ b/core/modules/language/lib/Drupal/language/LanguageNegotiationMethodBase.php @@ -0,0 +1,60 @@ +languageManager = $language_manager; + } + + /** + * {@inheritdoc} + */ + public function setConfig(ConfigFactory $config) { + $this->config = $config; + } + + /** + * {@inheritdoc} + */ + public function setCurrentUser(AccountInterface $current_user) { + $this->currentUser = $current_user; + } + +} diff --git a/core/modules/language/lib/Drupal/language/LanguageNegotiationMethodInterface.php b/core/modules/language/lib/Drupal/language/LanguageNegotiationMethodInterface.php new file mode 100644 index 0000000..cd19bb1 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/LanguageNegotiationMethodInterface.php @@ -0,0 +1,56 @@ +cacheBackend = $cache_backend; + $this->cacheKeyPrefix = 'language_negotiation_plugins'; + $this->cacheKey = 'language_negotiation_plugins'; + $this->alterInfo($module_handler, 'language_negotiation_info'); + } + +} diff --git a/core/modules/language/lib/Drupal/language/LanguageNegotiator.php b/core/modules/language/lib/Drupal/language/LanguageNegotiator.php new file mode 100644 index 0000000..855c8aa --- /dev/null +++ b/core/modules/language/lib/Drupal/language/LanguageNegotiator.php @@ -0,0 +1,366 @@ +languageManager = $language_manager; + $this->negotiatorManager = $negotiator_manager; + $this->configFactory = $config_factory; + $this->settings = $settings; + } + + /** + * Initializes the injected language manager with the negotiator. + * + * This should be called right after instantiating the negotiator to make it + * available to the language manager without introducing a circular + * dependency. + */ + public function initLanguageManager() { + $this->languageManager->setNegotiator($this); + } + + /** + * {@inheritdoc} + */ + public function reset() { + $this->negotiatedLanguages = array(); + $this->methods = array(); + } + + /** + * {@inheritdoc} + */ + public function setContext(AccountInterface $current_user, Request $request) { + $this->currentUser = $current_user; + $this->request = $request; + $this->reset(); + } + + /** + * {@inheritdoc} + */ + public function initializeType($type) { + $language = FALSE; + + if ($this->currentUser && $this->request) { + // Execute the language negotiation methods in the order they were set up + // and return the first valid language found. + foreach ($this->getConfiguration($type) as $method_id => $info) { + if (!isset($this->negotiatedLanguages[$method_id])) { + $this->negotiatedLanguages[$method_id] = $this->negotiateLanguage($type, $method_id); + } + + // Since objects are references, we need to return a clone to prevent + // the language negotiation method cache from being unintentionally + // altered. The same methods might be used with different language types + // based on configuration. + $language = !empty($this->negotiatedLanguages[$method_id]) ? clone($this->negotiatedLanguages[$method_id]) : FALSE; + + if ($language) { + // Remember the method ID used to detect the language. + $language->method_id = $method_id; + break; + } + } + } + + if (!$language) { + // If no other language was found use the default one. + $language = $this->languageManager->getDefaultLanguage(); + $language->method_id = LanguageNegotiatorInterface::METHOD_ID; + } + + return $language; + } + + /** + * {@inheritdoc} + */ + protected function getConfiguration($type) { + // @todo convert to CMI https://drupal.org/node/1827038 and + // https://drupal.org/node/2102477 + drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES, FALSE); + return variable_get("language_negotiation_$type", array()); + } + + /** + * Performs language negotiation using the specified negotiation method. + * + * @param string $type + * The language type to be initialized. + * @param string $method_id + * The string identifier of the language negotiation method to use to detect + * language. + * + * @return \Drupal\Core\Language\Language|FALSE + * Negotiated language object for given type and method, FALSE otherwise. + */ + protected function negotiateLanguage($type, $method_id) { + $langcode = FALSE; + $method = $this->negotiatorManager->getDefinition($method_id); + + if (!isset($method['types']) || in_array($type, $method['types'])) { + + // Check for a cache mode force from settings.php. + if ($this->settings->get('page_cache_without_database')) { + $cache_enabled = TRUE; + } + else { + $cache_enabled = $this->configFactory->get('system.performance')->get('cache.page.use_internal'); + } + + // If the language negotiation method has no cache preference or this is + // satisfied we can execute the callback. + if ($cache = !isset($method['cache']) || $this->currentUser->isAuthenticated() || $method['cache'] == $cache_enabled) { + $langcode = $this->getNegotiationMethodInstance($method_id)->getLangcode($this->request); + } + } + + $languages = $this->languageManager->getLanguages(); + return isset($languages[$langcode]) ? $languages[$langcode] : FALSE; + } + + /** + * {@inheritdoc} + */ + public function getNegotiationMethods($type = NULL) { + $definitions = $this->negotiatorManager->getDefinitions(); + if (isset($type)) { + $config = $this->getConfiguration($type); + $definitions = array_intersect_key($definitions, $config); + } + return $definitions; + } + + /** + * {@inheritdoc} + */ + public function getNegotiationMethodInstance($method_id) { + if (!isset($this->methods[$method_id])) { + $instance = $this->negotiatorManager->createInstance($method_id, array()); + $instance->setLanguageManager($this->languageManager); + $instance->setConfig($this->configFactory); + $instance->setCurrentUser($this->currentUser); + $this->methods[$method_id] = $instance; + } + return $this->methods[$method_id]; + } + + /** + * {@inheritdoc} + */ + public function getPrimaryNegotiationMethod($type) { + $config = $this->getConfiguration($type); + return empty($config) ? LanguageNegotiatorInterface::METHOD_ID : key($config); + } + + /** + * {@inheritdoc} + */ + public function isNegotiationMethodEnabled($method_id, $type = NULL) { + $enabled = FALSE; + $language_types = !empty($type) ? array($type) : $this->languageManager->getLanguageTypes(); + + foreach ($language_types as $type) { + $config = $this->getConfiguration($type); + if (isset($config[$method_id])) { + $enabled = TRUE; + break; + } + } + + return $enabled; + } + + /** + * {@inheritdoc} + */ + function saveConfiguration($type, $method_weights) { + $definitions = $this->getNegotiationMethods(); + $default_types = $this->languageManager->getLanguageTypes(); + + // Order the language negotiation method list by weight. + asort($method_weights); + foreach ($method_weights as $method_id => $weight) { + if (isset($definitions[$method_id])) { + $method = $definitions[$method_id]; + // If the language negotiation method does not express any preference + // about types, make it available for any configurable type. + $types = array_flip(!empty($method['types']) ? $method['types'] : $default_types); + // Check whether the method is defined and has the right type. + if (!isset($types[$type])) { + unset($method_weights[$method_id]); + } + } + else { + unset($method_weights[$method_id]); + } + } + + variable_set("language_negotiation_$type", $method_weights); + } + + /** + * {@inheritdoc} + */ + function purgeConfiguration() { + // Ensure that we are getting the defined language negotiation information. + // An invocation of \Drupal\Core\Extension\ModuleHandler::install() or + // \Drupal\Core\Extension\ModuleHandler::uninstall() could invalidate the + // cached information. + $this->negotiatorManager->clearCachedDefinitions(); + $this->languageManager->reset(); + foreach ($this->languageManager->getDefinedLanguageTypesInfo() as $type => $info) { + $this->saveConfiguration($type, $this->getConfiguration($type)); + } + } + + /** + * {@inheritdoc} + */ + function updateConfiguration(array $types) { + // Ensure that we are getting the defined language negotiation information. + // An invocation of \Drupal\Core\Extension\ModuleHandler::install() or + // \Drupal\Core\Extension\ModuleHandler::uninstall() could invalidate the + // cached information. + $this->negotiatorManager->clearCachedDefinitions(); + $this->languageManager->reset(); + + $language_types = array(); + $language_types_info = $this->languageManager->getDefinedLanguageTypesInfo(); + $method_definitions = $this->getNegotiationMethods(); + + foreach ($language_types_info as $type => $info) { + $configurable = in_array($type, $types); + + // Check whether the language type is unlocked. Only the status of + // unlocked language types can be toggled between configurable and + // non-configurable. The default language negotiation settings, if + // available, are stored in $info['fixed']. + if (empty($info['locked'])) { + // If we have a non-locked non-configurable language type without + // default language negotiation settings, we use the values negotiated + // for the interface language which should always be available. + if (!$configurable && !empty($info['fixed'])) { + $method_weights = array(LanguageNegotiationUI::METHOD_ID); + $method_weights = array_flip($method_weights); + $this->saveConfiguration($type, $method_weights); + } + } + else { + // Locked language types with default settings are always considered + // non-configurable. In turn if default settings are missing, the + // language type is always considered configurable. + $configurable = empty($info['fixed']); + + // If the language is non-configurable we need to store its language + // negotiation settings. + if (!$configurable) { + $method_weights = array(); + foreach ($info['fixed'] as $weight => $method_id) { + if (isset($method_definitions[$method_id])) { + $method_weights[$method_id] = $weight; + } + } + $this->saveConfiguration($type, $method_weights); + } + } + + $language_types[$type] = $configurable; + } + + // Store the language type configuration. + $config = array( + 'configurable' => array_keys(array_filter($language_types)), + 'all' => array_keys($language_types), + ); + $this->languageManager->saveLanguageTypesConfiguration($config); + } + +} diff --git a/core/modules/language/lib/Drupal/language/LanguageNegotiatorInterface.php b/core/modules/language/lib/Drupal/language/LanguageNegotiatorInterface.php new file mode 100644 index 0000000..f16ed54 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/LanguageNegotiatorInterface.php @@ -0,0 +1,211 @@ +query->has('q') && strtok($request->query->get('q'), '/') == 'admin') { + * return $this->languageManager->getDefaultLanguage()->id; + * } + * return $langcode; + * } + * } + * } + * ?> + * @endcode + * + * For more information, see + * @link http://drupal.org/node/1497272 Language Negotiation API @endlink + */ +interface LanguageNegotiatorInterface { + + /** + * The language negotiation method id for the language negotiator itself. + */ + const METHOD_ID = 'language-default'; + + /** + * Resets the negotiated languages and the method instances. + */ + public function reset(); + + /** + * Sets the contextual data and resets all language types. + * + * @param \Drupal\Core\Session\AccountInterface $current_user + * The current active user. + * @param \Symfony\Component\HttpFoundation\Request $request + * The HttpRequest object representing the current request. + */ + public function setContext(AccountInterface $current_user, Request $request); + + /** + * Initializes the specified language type. + * + * @param string $type + * The language type to be initialized. + * + * @return \Drupal\Core\Language\Language + * Return either the language of the specified type or the default language. + */ + public function initializeType($type); + + /** + * Returns the language negotiation methods enabled for a language type. + * + * @param string $type + * (optional) The language type. If no type is specified all the method + * definitions are returned. + * + * @return array + * An array of language negotiation method definitions keyed by method id. + */ + public function getNegotiationMethods($type = NULL); + + /** + * Returns an instance of the specified language negotiation method. + * + * @param string $method_id + * The method identifier. + * + * @return \Drupal\language\LanguageNegotiationMethodInterface + */ + public function getNegotiationMethodInstance($method_id); + + /** + * Returns the ID of the language type's primary language negotiation method. + * + * @param $type + * The language type. + * + * @return + * The identifier of the primary language negotiation method for the given + * language type, or the default method if none exists. + */ + public function getPrimaryNegotiationMethod($type); + + /** + * Checks whether a language negotiation method is enabled for a language type. + * + * @param $method_id + * The language negotiation method ID. + * @param $type + * (optional) The language type. If none is passed, all the configurable + * language types will be inspected. + * + * @return + * TRUE if the method is enabled for at least one of the given language + * types, or FALSE otherwise. + */ + public function isNegotiationMethodEnabled($method_id, $type = NULL); + + /** + * Saves a list of language negotiation methods for a language type. + * + * @param string $type + * The language type. + * @param array $method_weights + * An array of language negotiation method weights keyed by method ID. + */ + function saveConfiguration($type, $method_weights); + + /** + * Resave the configuration to purge missing negotiation methods. + */ + function purgeConfiguration(); + + /** + * Updates the configuration based on the given language types. + * + * @param array $types + * An array of configurable language types. + */ + function updateConfiguration(array $types); + +} diff --git a/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php b/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php new file mode 100644 index 0000000..d223580 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php @@ -0,0 +1,72 @@ +isMultilingual($container)) { + $container->register('language_request_subscriber', 'Drupal\language\EventSubscriber\LanguageRequestSubscriber') + ->addTag('event_subscriber') + ->addArgument(new Reference('language_manager')) + ->addArgument(new Reference('language_negotiator')) + ->addArgument(new Reference('string_translation')) + ->addArgument(new Reference('current_user')); + + $container->register('path_processor_language', 'Drupal\language\HttpKernel\PathProcessorLanguage') + ->addTag('path_processor_inbound', array('priority' => 300)) + ->addTag('path_processor_outbound', array('priority' => 100)) + ->addArgument(new Reference('config.factory')) + ->addArgument(new Reference('settings')) + ->addArgument(new Reference('language_manager')) + ->addArgument(new Reference('language_negotiator')) + ->addArgument(new Reference('current_user')); + } + } + + /** + * {@inheritdoc} + */ + public function alter(ContainerBuilder $container) { + $definition = $container->getDefinition('language_manager'); + $definition->setClass('Drupal\language\ConfigurableLanguageManager') + ->addArgument(new Reference('config.factory')) + ->addArgument(new Reference('module_handler')) + ->addMethodCall('initConfigOverrides'); + } + + /** + * Checks whether the site is multilingual. + * + * @param \Drupal\Core\DependencyInjection\ContainerBuilder $container + * The container builder services are being registered to. + * + * @return bool + * TRUE if the site is multilingual, FALSE otherwise. + */ + protected function isMultilingual(ContainerBuilder $container) { + $prefix = 'language.entity.'; + $config_ids = array_filter($container->get('kernel.config.storage')->listAll($prefix), function($config_id) use ($prefix) { + return $config_id != $prefix . Language::LANGCODE_NOT_SPECIFIED && $config_id != $prefix . Language::LANGCODE_NOT_APPLICABLE; + }); + return count($config_ids) > 1; + } + +} diff --git a/core/modules/language/lib/Drupal/language/LanguageSwitcherInterface.php b/core/modules/language/lib/Drupal/language/LanguageSwitcherInterface.php new file mode 100644 index 0000000..d6edd5b --- /dev/null +++ b/core/modules/language/lib/Drupal/language/LanguageSwitcherInterface.php @@ -0,0 +1,32 @@ +languageManager = $language_manager; + } + + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('language_manager') + ); + } + /** * {@inheritdoc} */ function access(AccountInterface $account) { - return language_multilingual(); + return $this->languageManager->isMultilingual(); } /** @@ -36,7 +77,7 @@ public function build() { $build = array(); $path = drupal_is_front_page() ? '' : current_path(); $type = $this->getDerivativeId(); - $links = language_negotiation_get_switch_links($type, $path); + $links = $this->languageManager->getLanguageSwitchLinks($type, $path); if (isset($links->links)) { $build = array( diff --git a/core/modules/language/lib/Drupal/language/Plugin/Condition/Language.php b/core/modules/language/lib/Drupal/language/Plugin/Condition/Language.php index 7f4289e..9dda571 100644 --- a/core/modules/language/lib/Drupal/language/Plugin/Condition/Language.php +++ b/core/modules/language/lib/Drupal/language/Plugin/Condition/Language.php @@ -30,7 +30,7 @@ class Language extends ConditionPluginBase { */ public function buildForm(array $form, array &$form_state) { $form = parent::buildForm($form, $form_state); - if (language_multilingual()) { + if (\Drupal::languageManager()->isMultilingual()) { // Fetch languages. $languages = language_list(Lang::STATE_ALL); $langcodes_options = array(); diff --git a/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php b/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php index a329c3b..acf276f 100644 --- a/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php +++ b/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php @@ -8,28 +8,35 @@ namespace Drupal\language\Plugin\Derivative; use Drupal\Component\Plugin\Derivative\DerivativeBase; +use Drupal\language\ConfigurableLanguageManagerInterface; /** * Provides language switcher block plugin definitions for all languages. */ class LanguageBlock extends DerivativeBase { + /** * {@inheritdoc} */ public function getDerivativeDefinitions(array $base_plugin_definition) { - include_once DRUPAL_ROOT . '/core/includes/language.inc'; - $info = language_types_info(); - $configurable_types = language_types_get_configurable(); - foreach ($configurable_types as $type) { - $this->derivatives[$type] = $base_plugin_definition; - $this->derivatives[$type]['admin_label'] = t('Language switcher (!type)', array('!type' => $info[$type]['name'])); - $this->derivatives[$type]['cache'] = DRUPAL_NO_CACHE; - } - // If there is just one configurable type then change the title of the - // block. - if (count($configurable_types) == 1) { - $this->derivatives[reset($configurable_types)]['admin_label'] = t('Language switcher'); + $language_manager = \Drupal::languageManager(); + + if ($language_manager instanceof ConfigurableLanguageManagerInterface) { + $info = $language_manager->getDefinedLanguageTypesInfo(); + $configurable_types = $language_manager->getLanguageTypes(); + foreach ($configurable_types as $type) { + $this->derivatives[$type] = $base_plugin_definition; + $this->derivatives[$type]['admin_label'] = t('Language switcher (!type)', array('!type' => $info[$type]['name'])); + $this->derivatives[$type]['cache'] = DRUPAL_NO_CACHE; + } + // If there is just one configurable type then change the title of the + // block. + if (count($configurable_types) == 1) { + $this->derivatives[reset($configurable_types)]['admin_label'] = t('Language switcher'); + } } + return parent::getDerivativeDefinitions($base_plugin_definition); } + } diff --git a/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php new file mode 100644 index 0000000..85d49c5 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php @@ -0,0 +1,49 @@ +languageManager && $request && $request->server->get('HTTP_ACCEPT_LANGUAGE')) { + $http_accept_language = $request->server->get('HTTP_ACCEPT_LANGUAGE'); + $langcodes = array_keys($this->languageManager->getLanguages()); + $mappings = $this->config->get('language.mappings')->get(); + $langcode = Browser::getLangcode($http_accept_language, $langcodes, $mappings); + } + + return $langcode; + } + +} diff --git a/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php new file mode 100644 index 0000000..fba2a33 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php @@ -0,0 +1,48 @@ +languageManager) { + $languages = $this->languageManager->getLanguages(); + $langcode = $this->config->get('language.negotiation')->get('selected_langcode'); + if (!isset($languages[$langcode])) { + $langcode = $this->languageManager->getDefaultLanguage()->id; + } + } + + return $langcode; + } + +} diff --git a/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php new file mode 100644 index 0000000..dd7e5b4 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php @@ -0,0 +1,146 @@ +config->get('language.negotiation')->get('session'); + $param = $config['parameter']; + $langcode = $request && $request->query->get($param) ? $request->query->get($param) : FALSE; + + // We need to update the session parameter with the request value only if we + // have an authenticated user. + if ($langcode && $this->languageManager) { + $languages = $this->languageManager->getLanguages(); + if ($this->currentUser->isAuthenticated() && isset($languages[$langcode])) { + $_SESSION[$param] = $langcode; + } + } + + // Session parameter. + if (isset($_SESSION[$param])) { + $langcode = $_SESSION[$param]; + } + + return $langcode; + } + + /** + * {@inheritdoc} + */ + public function processOutbound($path, &$options = array(), Request $request = NULL) { + if ($request) { + // The following values are not supposed to change during a single page + // request processing. + if (!isset($this->queryRewrite)) { + if ($this->currentUser->isAnonymous()) { + $languages = $this->languageManager->getLanguages(); + $config = $this->config->get('language.negotiation')->get('session'); + $this->queryParam = $config['parameter']; + $this->queryValue = $request->query->has($this->queryParam) ? $request->query->get($this->queryParam) : NULL; + $this->queryRewrite = isset($languages[$this->queryValue]); + } + else { + $this->queryRewrite = FALSE; + } + } + + // If the user is anonymous, the user language negotiation method is + // enabled, and the corresponding option has been set, we must preserve + // any explicit user language preference even with cookies disabled. + if ($this->queryRewrite) { + if (isset($options['query']) && is_string($options['query'])) { + $query = array(); + parse_str($options['query'], $query); + $options['query'] = $query; + } + if (!isset($options['query'][$this->queryParam])) { + $options['query'][$this->queryParam] = $this->queryValue; + } + } + } + return $path; + } + + /** + * {@inheritdoc} + */ + function getLanguageSwitchLinks(Request $request, $type, $path) { + $links = array(); + $config = $this->config->get('language.negotiation')->get('session'); + $param = $config['parameter']; + $language_query = isset($_SESSION[$param]) ? $_SESSION[$param] : $this->languageManager->getCurrentLanguage($type)->id; + $query = array(); + parse_str($request->getQueryString(), $query); + + foreach ($this->languageManager->getLanguages() as $language) { + $langcode = $language->id; + $links[$langcode] = array( + 'href' => $path, + 'title' => $language->name, + 'attributes' => array('class' => array('language-link')), + 'query' => $query, + ); + if ($language_query != $langcode) { + $links[$langcode]['query'][$param] = $langcode; + } + else { + $links[$langcode]['attributes']['class'][] = ' session-active'; + } + } + + return $links; + } + +} diff --git a/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php new file mode 100644 index 0000000..c3e5aeb --- /dev/null +++ b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php @@ -0,0 +1,38 @@ +languageManager ? $this->languageManager->getCurrentLanguage()->id : FALSE; + } + +} diff --git a/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php new file mode 100644 index 0000000..7cb8bc7 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php @@ -0,0 +1,203 @@ +languageManager) { + $languages = $this->languageManager->getLanguages(); + $config = $this->config->get('language.negotiation')->get('url'); + + switch ($config['source']) { + case LanguageNegotiationUrl::CONFIG_PATH_PREFIX: + $request_path = urldecode(trim($request->getPathInfo(), '/')); + $path_args = explode('/', $request_path); + $prefix = array_shift($path_args); + + // Search prefix within enabled languages. + $negotiated_language = FALSE; + foreach ($languages as $language) { + if (isset($config['prefixes'][$language->id]) && $config['prefixes'][$language->id] == $prefix) { + $negotiated_language = $language; + break; + } + } + + if ($negotiated_language !== FALSE && $negotiated_language instanceof \Drupal\Core\Language\Language) { + $langcode = $negotiated_language->id; + } + break; + + case LanguageNegotiationUrl::CONFIG_DOMAIN: + // Get only the host, not the port. + $http_host = $request->getHost(); + foreach ($languages as $language) { + // Skip the check if the language doesn't have a domain. + if (!empty($config['domains'][$language->id])) { + // Ensure that there is exactly one protocol in the URL when + // checking the hostname. + $host = 'http://' . str_replace(array('http://', 'https://'), '', $config['domains'][$language->id]); + $host = parse_url($host, PHP_URL_HOST); + if ($http_host == $host) { + $langcode = $language->id; + break; + } + } + } + break; + } + } + + return $langcode; + } + + /** + * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processInbound(). + */ + public function processInbound($path, Request $request) { + $config = $this->config->get('language.negotiation')->get('url'); + $parts = explode('/', $path); + $prefix = array_shift($parts); + + // Search prefix within enabled languages. + foreach ($this->languageManager->getLanguages() as $language) { + if (isset($config['prefixes'][$language->id]) && $config['prefixes'][$language->id] == $prefix) { + // Rebuild $path with the language removed. + $path = implode('/', $parts); + break; + } + } + + return $path; + } + + /** + * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processOutbound(). + */ + public function processOutbound($path, &$options = array(), Request $request = NULL) { + $url_scheme = 'http'; + $port = 80; + if ($request) { + $url_scheme = $request->getScheme(); + $port = $request->getPort(); + } + $languages = array_flip(array_keys($this->languageManager->getLanguages())); + // Language can be passed as an option, or we go for current URL language. + if (!isset($options['language'])) { + $language_url = $this->languageManager->getCurrentLanguage(Language::TYPE_URL); + $options['language'] = $language_url; + } + // We allow only enabled languages here. + elseif (is_object($options['language']) && !isset($languages[$options['language']->id])) { + return $path; + } + $config = $this->config->get('language.negotiation')->get('url'); + if ($config['source'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX) { + if (is_object($options['language']) && !empty($config['prefixes'][$options['language']->id])) { + return empty($path) ? $config['prefixes'][$options['language']->id] : $config['prefixes'][$options['language']->id] . '/' . $path; + } + } + elseif ($config['source'] == LanguageNegotiationUrl::CONFIG_DOMAIN) { + if (is_object($options['language']) && !empty($config['domains'][$options['language']->id])) { + + // Save the original base URL. If it contains a port, we need to + // retain it below. + if (!empty($options['base_url'])) { + // The colon in the URL scheme messes up the port checking below. + $normalized_base_url = str_replace(array('https://', 'http://'), '', $options['base_url']); + } + + // Ask for an absolute URL with our modified base URL. + $options['absolute'] = TRUE; + $options['base_url'] = $url_scheme . '://' . $config['domains'][$options['language']->id]; + + // In case either the original base URL or the HTTP host contains a + // port, retain it. + if (isset($normalized_base_url) && strpos($normalized_base_url, ':') !== FALSE) { + list(, $port) = explode(':', $normalized_base_url); + $options['base_url'] .= ':' . $port; + } + elseif ($port != 80) { + $options['base_url'] .= ':' . $port; + } + + if (isset($options['https']) && !empty($options['mixed_mode_sessions'])) { + if ($options['https'] === TRUE) { + $options['base_url'] = str_replace('http://', 'https://', $options['base_url']); + } + elseif ($options['https'] === FALSE) { + $options['base_url'] = str_replace('https://', 'http://', $options['base_url']); + } + } + + // Add Drupal's subfolder from the base_path if there is one. + $options['base_url'] .= rtrim(base_path(), '/'); + } + } + return $path; + } + + /** + * {@inheritdoc} + */ + function getLanguageSwitchLinks(Request $request, $type, $path) { + $links = array(); + + foreach ($this->languageManager->getLanguages() as $language) { + $links[$language->id] = array( + 'href' => $path, + 'title' => $language->name, + 'language' => $language, + 'attributes' => array('class' => array('language-link')), + ); + } + + return $links; + } + +} diff --git a/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUrlFallback.php b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUrlFallback.php new file mode 100644 index 0000000..379cf79 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUrlFallback.php @@ -0,0 +1,76 @@ +languageManager) { + $default = $this->languageManager->getDefaultLanguage(); + $config = $this->config->get('language.negotiation')->get('url'); + $prefix = ($config['source'] == LanguageNegotiationUrl::CONFIG_PATH_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($config['prefixes'][$default->id])) || (!$prefix && empty($config['domains'][$default->id]))) { + $langcode = $default->id; + } + else { + $langcode = $this->languageManager->getCurrentLanguage()->id; + } + } + + return $langcode; + } + +} diff --git a/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUser.php b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUser.php new file mode 100644 index 0000000..2e267e3 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUser.php @@ -0,0 +1,50 @@ +languageManager && $this->currentUser->isAuthenticated()) { + $preferred_langcode = $this->currentUser->getPreferredLangcode(); + $default_langcode = $this->languageManager->getDefaultLanguage()->id; + $languages = $this->languageManager->getLanguages(); + if (!empty($preferred_langcode) && $preferred_langcode != $default_langcode && isset($languages[$preferred_langcode])) { + $langcode = $preferred_langcode; + } + } + + // No language preference from the user. + return $langcode; + } + +} diff --git a/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php new file mode 100644 index 0000000..fd1639c --- /dev/null +++ b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php @@ -0,0 +1,69 @@ +languageManager && $this->currentUser->isAuthenticated() && $this->isAdminPath($request)) { + $preferred_admin_langcode = $this->currentUser->getPreferredAdminLangcode(); + $default_langcode = $this->languageManager->getDefaultLanguage()->id; + $languages = $this->languageManager->getLanguages(); + if (!empty($preferred_admin_langcode) && $preferred_admin_langcode != $default_langcode && isset($languages[$preferred_admin_langcode])) { + $langcode = $preferred_admin_langcode; + } + } + + // No language preference from the user or not on an admin path. + return $langcode; + } + + /** + * Checks whether the given path is an administrative one. + * + * @param string $path + * A Drupal path. + * + * @return bool + * TRUE if the path is administrative, FALSE otherwise. + */ + public function isAdminPath(Request $request) { + $result = FALSE; + if ($request && function_exists('path_is_admin')) { + $path = urldecode(trim($request->getPathInfo(), '/')); + $result = path_is_admin($path); + } + return $result; + } + +} diff --git a/core/modules/language/lib/Drupal/language/Tests/Condition/LanguageConditionTest.php b/core/modules/language/lib/Drupal/language/Tests/Condition/LanguageConditionTest.php index 19449d8..ed029fa 100644 --- a/core/modules/language/lib/Drupal/language/Tests/Condition/LanguageConditionTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/Condition/LanguageConditionTest.php @@ -26,7 +26,7 @@ class LanguageConditionTest extends DrupalUnitTestBase { /** * The language manager. * - * @var \Drupal\Core\Language\LanguageManager + * @var \Drupal\Core\Language\LanguageManagerInterface */ protected $languageManager; diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php index 679d6f9..f660eb8 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php @@ -7,8 +7,10 @@ namespace Drupal\language\Tests; -use Drupal\simpletest\WebTestBase; +use Drupal\Component\Utility\Browser; use Drupal\Core\Language\Language; +use Drupal\simpletest\WebTestBase; +use Symfony\Component\HttpFoundation\Request; /** * Test browser language detection. @@ -153,9 +155,9 @@ function testLanguageFromBrowser() { 'zh-cht' => 'zh-hant', ); + $mappings = $this->container->get('config.factory')->get('language.mappings')->get(); foreach ($test_cases as $accept_language => $expected_result) { - \Drupal::request()->server->set('HTTP_ACCEPT_LANGUAGE', $accept_language); - $result = language_from_browser($languages); + $result = Browser::getLangcode($accept_language, array_keys($languages), $mappings); $this->assertIdentical($result, $expected_result, format_string("Language selection '@accept-language' selects '@result', result = '@actual'", array('@accept-language' => $accept_language, '@result' => $expected_result, '@actual' => isset($result) ? $result : 'none'))); } } diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php index 1fb8c85..6f9c552 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php @@ -72,7 +72,7 @@ function testLanguageConfiguration() { ); $this->drupalPostForm(NULL, $edit, t('Save configuration')); $this->assertOptionSelected('edit-site-default-language', 'fr', 'Default language updated.'); - $this->assertEqual($this->getUrl(), url('admin/config/regional/settings', array('absolute' => TRUE)), 'Correct page redirection.'); + $this->assertEqual($this->getUrl(), url('fr/admin/config/regional/settings', array('absolute' => TRUE)), 'Correct page redirection.'); // Check if a valid language prefix is added after changing the default // language. @@ -156,10 +156,10 @@ function testLanguageConfigurationWeight() { */ protected function checkConfigurableLanguageWeight($state = 'by default') { // Reset language list. - drupal_static_reset('language_list'); + \Drupal::languageManager()->reset(); $max_configurable_language_weight = $this->getHighestConfigurableLanguageWeight(); $replacements = array('@event' => $state); - foreach (language_list(Language::STATE_LOCKED) as $locked_language) { + foreach (\Drupal::languageManager()->getLanguages(Language::STATE_LOCKED) as $locked_language) { $replacements['%language'] = $locked_language->name; $this->assertTrue($locked_language->weight > $max_configurable_language_weight, format_string('System language %language has higher weight than configurable languages @event', $replacements)); } diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageDependencyInjectionTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageDependencyInjectionTest.php index ca49673..969a836 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageDependencyInjectionTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageDependencyInjectionTest.php @@ -9,36 +9,23 @@ use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Language\Language; -use Drupal\simpletest\WebTestBase; /** * Test for dependency injected language object. */ -class LanguageDependencyInjectionTest extends WebTestBase { +class LanguageDependencyInjectionTest extends LanguageTestBase { /** - * Modules to enable. - * - * @var array + * {@inheritdoc} */ - public static $modules = array('language'); - public static function getInfo() { return array( - 'name' => 'Language dependency injection', - 'description' => 'Compares the default language from $GLOBALS against the dependency injected language object.', - 'group' => 'Language', + 'name' => 'Language dependency injection', + 'description' => 'Compares the default language from $GLOBALS against the dependency injected language object.', + 'group' => 'Language', ); } - function setUp() { - parent::setUp(); - - // Ensure we are building a new Language object for each test. - $this->container->get('language_manager')->reset(); - } - - /** * Test dependency injected languages against a new Language object. * @@ -48,8 +35,8 @@ function testDependencyInjectedNewLanguage() { // Initialize the language system. drupal_language_initialize(); - $expected = language_default(); - $result = language(Language::TYPE_INTERFACE); + $expected = $this->languageManager->getDefaultLanguage(); + $result = $this->languageManager->getCurrentLanguage(); foreach ($expected as $property => $value) { $this->assertEqual($expected->$property, $result->$property, format_string('The dependency injected language object %prop property equals the new Language object %prop property.', array('%prop' => $property))); } @@ -74,12 +61,12 @@ function testDependencyInjectedNewDefaultLanguage() { variable_set('language_default', $new_language_default); // Initialize the language system. - drupal_language_initialize(); + $this->languageManager->init(); // The language system creates a Language object which contains the // same properties as the new default language object. $expected = new Language($new_language_default); - $result = language(Language::TYPE_INTERFACE); + $result = $this->languageManager->getCurrentLanguage(); foreach ($expected as $property => $value) { $this->assertEqual($expected->$property, $result->$property, format_string('The dependency injected language object %prop property equals the default language object %prop property.', array('%prop' => $property))); } @@ -87,4 +74,5 @@ function testDependencyInjectedNewDefaultLanguage() { // Delete the language_default variable we previously set. variable_del('language_default'); } + } diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageFallbackTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageFallbackTest.php index 4123fc0..fb621d8 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageFallbackTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageFallbackTest.php @@ -8,12 +8,11 @@ namespace Drupal\language\Tests; use Drupal\Core\Language\Language; -use Drupal\simpletest\DrupalUnitTestBase; /** * Tests the language fallback behavior. */ -class LanguageFallbackTest extends DrupalUnitTestBase { +class LanguageFallbackTest extends LanguageTestBase { public static function getInfo() { return array( @@ -24,23 +23,11 @@ public static function getInfo() { } /** - * The state storage service. - * - * @var \Drupal\Core\KeyValueStore\StateInterface - */ - protected $state; - - /** * {@inheritdoc} */ protected function setUp() { parent::setUp(); - $this->enableModules(array('language', 'language_test')); - $this->installConfig(array('language')); - - $this->state = $this->container->get('state'); - for ($i = 0; $i < 3; $i++) { $language = new Language(); $language->id = $this->randomName(2); @@ -53,18 +40,18 @@ protected function setUp() { * Tests language fallback candidates. */ public function testCandidates() { - $manager = $this->getLanguageManager(); - $expected = array_keys(language_list() + array(Language::LANGCODE_NOT_SPECIFIED => NULL)); + $language_list = $this->languageManager->getLanguages(); + $expected = array_keys($language_list + array(Language::LANGCODE_NOT_SPECIFIED => NULL)); // Check that language fallback candidates by default are all the available // languages sorted by weight. - $candidates = $manager->getFallbackCandidates(); + $candidates = $this->languageManager->getFallbackCandidates(); $this->assertEqual(array_values($candidates), $expected, 'Language fallback candidates are properly returned.'); // Check that candidates are alterable. $this->state->set('language_test.fallback_alter.candidates', TRUE); $expected = array_slice($expected, 0, count($expected) - 1); - $candidates = $manager->getFallbackCandidates(); + $candidates = $this->languageManager->getFallbackCandidates(); $this->assertEqual(array_values($candidates), $expected, 'Language fallback candidates are alterable.'); // Check that candidates are alterable for specific operations. @@ -72,28 +59,18 @@ public function testCandidates() { $this->state->set('language_test.fallback_operation_alter.candidates', TRUE); $expected[] = Language::LANGCODE_NOT_SPECIFIED; $expected[] = Language::LANGCODE_NOT_APPLICABLE; - $candidates = $manager->getFallbackCandidates(NULL, array('operation' => 'test')); + $candidates = $this->languageManager->getFallbackCandidates(NULL, array('operation' => 'test')); $this->assertEqual(array_values($candidates), $expected, 'Language fallback candidates are alterable for specific operations.'); // Check that when the site is monolingual no language fallback is applied. - $default_langcode = language_default()->id; - foreach (language_list() as $langcode => $language) { + $default_langcode = $this->languageManager->getDefaultLanguage()->id; + foreach ($language_list as $langcode => $language) { if ($langcode != $default_langcode) { language_delete($langcode); } } - $candidates = $this->getLanguageManager()->getFallbackCandidates(); + $candidates = $this->languageManager->getFallbackCandidates(); $this->assertEqual(array_values($candidates), array(Language::LANGCODE_DEFAULT), 'Language fallback is not applied when the Language module is not enabled.'); } - /** - * Returns the language manager service. - * - * @return \Drupal\Core\Language\LanguageManager - * The language manager. - */ - protected function getLanguageManager() { - return $this->container->get('language_manager'); - } - } diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php index 8b2190d..a475c04 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php @@ -72,11 +72,11 @@ function testLanguageList() { ); $this->drupalPostForm(NULL, $edit, t('Save configuration')); $this->assertNoOptionSelected('edit-site-default-language', 'en', 'Default language updated.'); - $this->assertEqual($this->getUrl(), url($path, array('absolute' => TRUE)), 'Correct page redirection.'); + $this->assertEqual($this->getUrl(), url($langcode . '/' . $path, array('absolute' => TRUE)), 'Correct page redirection.'); // Ensure we can't delete the default language. $this->drupalGet('admin/config/regional/language/delete/' . $langcode); - $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); + $this->assertEqual($this->getUrl(), url($langcode . '/admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); $this->assertText(t('The default language cannot be deleted.'), 'Failed to delete the default language.'); // Ensure 'Edit' link works. @@ -89,7 +89,7 @@ function testLanguageList() { ); $this->drupalPostForm('admin/config/regional/language/edit/' . $langcode, $edit, t('Save language')); $this->assertRaw($name, 'The language has been updated.'); - $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); + $this->assertEqual($this->getUrl(), url($langcode . '/admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); // Change back the default language. $edit = array( @@ -104,7 +104,7 @@ function testLanguageList() { $this->drupalGet('admin/config/regional/language/delete/' . $langcode); // First test the 'cancel' link. $this->clickLink(t('Cancel')); - $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); + $this->assertEqual($this->getUrl(), url('en/admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); $this->assertRaw($name, 'The language was not deleted.'); // Delete the language for real. This a confirm form, we do not need any // fields changed. @@ -112,19 +112,17 @@ function testLanguageList() { // We need raw here because %language and %langcode will add HTML. $t_args = array('%language' => $name, '%langcode' => $langcode); $this->assertRaw(t('The %language (%langcode) language has been removed.', $t_args), 'The test language has been removed.'); - $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); + $this->assertEqual($this->getUrl(), url('en/admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); // Verify that language is no longer found. $this->drupalGet('admin/config/regional/language/delete/' . $langcode); $this->assertResponse(404, 'Language no longer found.'); // Make sure the "language_count" state has been updated correctly. - drupal_static_reset('language_list'); + $this->container->get('language_manager')->reset(); $languages = language_list(); - $language_count = $this->container->get('state')->get('language_count') ?: 1; - $this->assertEqual($language_count, count($languages), 'Language count is correct.'); // Delete French. $this->drupalPostForm('admin/config/regional/language/delete/fr', array(), t('Delete')); // Get the count of languages. - drupal_static_reset('language_list'); + $this->container->get('language_manager')->reset(); $languages = language_list(); // We need raw here because %language and %langcode will add HTML. $t_args = array('%language' => 'French', '%langcode' => 'fr'); @@ -134,8 +132,6 @@ function testLanguageList() { $this->drupalGet('admin/config/regional/language/delete/fr'); $this->assertResponse(404, 'Language no longer found.'); // Make sure the "language_count" state has not changed. - $language_count = $this->container->get('state')->get('language_count') ?: 1; - $this->assertEqual($language_count, count($languages), 'Language count is correct.'); // Ensure we can delete the English language. Right now English is the only // language so we must add a new language and make it the default before @@ -162,7 +158,7 @@ function testLanguageList() { ); $this->drupalPostForm(NULL, $edit, t('Save configuration')); $this->assertNoOptionSelected('edit-site-default-language', 'en', 'Default language updated.'); - $this->assertEqual($this->getUrl(), url($path, array('absolute' => TRUE)), 'Correct page redirection.'); + $this->assertEqual($this->getUrl(), url($langcode . '/' . $path, array('absolute' => TRUE)), 'Correct page redirection.'); $this->drupalPostForm('admin/config/regional/language/delete/en', array(), t('Delete')); // We need raw here because %language and %langcode will add HTML. diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php index 6cff106..471dab2 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php @@ -8,6 +8,7 @@ namespace Drupal\language\Tests; use Drupal\Core\Language\Language; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUI; use Drupal\simpletest\WebTestBase; /** @@ -22,6 +23,16 @@ class LanguageNegotiationInfoTest extends WebTestBase { */ public static $modules = array('language'); + /** + * The language manager. + * + * @var \Drupal\language\ConfigurableLanguageManagerInterface + */ + protected $languageManager; + + /** + * {@inheritdoc} + */ public static function getInfo() { return array( 'name' => 'Language negotiation info', @@ -30,9 +41,12 @@ public static function getInfo() { ); } + /** + * {@inheritdoc} + */ function setUp() { parent::setUp(); - require_once DRUPAL_ROOT .'/core/includes/language.inc'; + $this->languageManager = $this->container->get('language_manager'); $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'view the administration theme')); $this->drupalLogin($admin_user); $this->drupalPostForm('admin/config/regional/language/add', array('predefined_langcode' => 'it'), t('Add language')); @@ -56,13 +70,13 @@ function testInfoAlterations() { \Drupal::state()->set('language_test.content_language_type', TRUE); $this->languageNegotiationUpdate(); $type = Language::TYPE_CONTENT; - $language_types = language_types_get_configurable(); + $language_types = $this->languageManager->getLanguageTypes(); $this->assertTrue(in_array($type, $language_types), 'Content language type is configurable.'); // Enable some core and custom language negotiation methods. The test // language type is supposed to be configurable. $test_type = 'test_language_type'; - $interface_method_id = LANGUAGE_NEGOTIATION_INTERFACE; + $interface_method_id = LanguageNegotiationUI::METHOD_ID; $test_method_id = 'test_language_negotiation_method'; $form_field = $type . '[enabled]['. $interface_method_id .']'; $edit = array( @@ -83,7 +97,7 @@ function testInfoAlterations() { // Check that type-specific language negotiation methods can be assigned // only to the corresponding language types. - foreach (language_types_get_configurable() as $type) { + foreach ($this->languageManager->getLanguageTypes() as $type) { $form_field = $type . '[enabled][test_language_negotiation_method_ts]'; if ($type == $test_type) { $this->assertFieldByXPath("//input[@name=\"$form_field\"]", NULL, format_string('Type-specific test language negotiation method available for %type.', array('%type' => $type))); @@ -96,7 +110,7 @@ function testInfoAlterations() { // Check language negotiation results. $this->drupalGet(''); $last = \Drupal::state()->get('language_test.language_negotiation_last'); - foreach (language_types_get_all() as $type) { + foreach ($this->languageManager->getDefinedLanguageTypes() as $type) { $langcode = $last[$type]; $value = $type == Language::TYPE_CONTENT || strpos($type, 'test') !== FALSE ? 'it' : 'en'; $this->assertEqual($langcode, $value, format_string('The negotiated language for %type is %language', array('%type' => $type, '%language' => $value))); @@ -107,7 +121,7 @@ function testInfoAlterations() { $this->languageNegotiationUpdate('uninstall'); // Check that only the core language types are available. - foreach (language_types_get_all() as $type) { + foreach ($this->languageManager->getDefinedLanguageTypes() as $type) { $this->assertTrue(strpos($type, 'test') === FALSE, format_string('The %type language is still available', array('%type' => $type))); } @@ -139,17 +153,16 @@ protected function languageNegotiationUpdate($op = 'install') { // Install/uninstall language_test only if we did not already before. if ($last_op != $op) { call_user_func(array($this->container->get('module_handler'), $op), $modules); - // Reset hook implementation cache. - $this->container->get('module_handler')->resetImplementations(); + $last_op = $op; } - - drupal_static_reset('language_types_info'); - drupal_static_reset('language_negotiation_info'); - $function = "language_modules_{$op}ed"; - if (function_exists($function)) { - $function($modules); + else { + $function = "language_modules_{$op}ed"; + if (function_exists($function)) { + $function($modules); + } } + $this->languageManager->reset(); $this->drupalGet('admin/config/regional/language/detection'); } @@ -157,9 +170,8 @@ protected function languageNegotiationUpdate($op = 'install') { * Check that language negotiation for fixed types matches the stored one. */ protected function checkFixedLanguageTypes() { - drupal_static_reset('language_types_info'); - $configurable = language_types_get_configurable(); - foreach (language_types_info() as $type => $info) { + $configurable = $this->languageManager->getLanguageTypes(); + foreach ($this->languageManager->getDefinedLanguageTypesInfo() as $type => $info) { if (!in_array($type, $configurable) && isset($info['fixed'])) { $negotiation = variable_get("language_negotiation_$type", array()); $equal = count($info['fixed']) == count($negotiation); diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguagePathMonolingualTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguagePathMonolingualTest.php index 731ccf0..840f8fc 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguagePathMonolingualTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguagePathMonolingualTest.php @@ -51,7 +51,8 @@ function setUp() { $this->drupalPostForm('admin/config/regional/language/delete/en', array(), t('Delete')); // Verify that French is the only language. - $this->assertFalse(language_multilingual(), 'Site is mono-lingual'); + $this->container->get('language_manager')->reset(); + $this->assertFalse(\Drupal::languageManager()->isMultilingual(), 'Site is mono-lingual'); $this->assertEqual(language_default()->id, 'fr', 'French is the default language'); // Set language detection to URL. diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageTestBase.php b/core/modules/language/lib/Drupal/language/Tests/LanguageTestBase.php new file mode 100644 index 0000000..70a5122 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageTestBase.php @@ -0,0 +1,48 @@ +enableModules(array('system', 'language', 'language_test')); + $this->installSchema('system', array('variable')); + $this->installConfig(array('language')); + + $this->state = $this->container->get('state'); + + // Ensure we are building a new Language object for each test. + $this->languageManager = $this->container->get('language_manager'); + $this->languageManager->reset(); + } + +} diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php index 7c037b5..9e3cdab 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php @@ -7,6 +7,11 @@ namespace Drupal\language\Tests; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSelected; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUser; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin; use Drupal\simpletest\WebTestBase; use Drupal\Core\Language\Language; use Symfony\Component\HttpFoundation\Request; @@ -53,7 +58,7 @@ class LanguageUILanguageNegotiationTest extends WebTestBase { public static function getInfo() { return array( 'name' => 'UI language negotiation', - 'description' => 'Test UI language switching by URL path prefix and domain.', + 'description' => 'Test UI language switching.', 'group' => 'Language', ); } @@ -64,7 +69,6 @@ function setUp() { $this->request = Request::create('http://example.com/'); $this->container->set('request', $this->request); - require_once DRUPAL_ROOT . '/core/includes/language.inc'; $admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages', 'administer blocks')); $this->drupalLogin($admin_user); } @@ -106,7 +110,7 @@ function testUILanguageNegotiation() { // into database when seen by t(). Without doing this, our target string // is for some reason not found when doing translate search. This might // be some bug. - drupal_static_reset('language_list'); + $this->container->get('language_manager')->reset(); $languages = language_list(); variable_set('language_default', (array) $languages['vi']); // First visit this page to make sure our target string is searchable. @@ -142,17 +146,14 @@ function testUILanguageNegotiation() { ); $this->drupalPostForm('admin/config/regional/translate', $edit, t('Save translations')); - // Configure URL language rewrite. - variable_set('language_negotiation_url_type', Language::TYPE_INTERFACE); - // Configure selected language negotiation to use zh-hans. $edit = array('selected_langcode' => $langcode); $this->drupalPostForm('admin/config/regional/language/detection/selected', $edit, t('Save configuration')); $test = array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_SELECTED), + 'language_negotiation' => array(LanguageNegotiationSelected::METHOD_ID), 'path' => 'admin/config', 'expect' => $language_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED, + 'expected_method_id' => LanguageNegotiationSelected::METHOD_ID, 'http_header' => $http_header_browser_fallback, 'message' => 'SELECTED: UI language is switched based on selected language.', ); @@ -161,10 +162,10 @@ function testUILanguageNegotiation() { // An invalid language is selected. \Drupal::config('language.negotiation')->set('selected_langcode', NULL)->save(); $test = array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_SELECTED), + 'language_negotiation' => array(LanguageNegotiationSelected::METHOD_ID), 'path' => 'admin/config', 'expect' => $default_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED, + 'expected_method_id' => LanguageNegotiationSelected::METHOD_ID, 'http_header' => $http_header_browser_fallback, 'message' => 'SELECTED > DEFAULT: UI language is switched based on selected language.', ); @@ -173,10 +174,10 @@ function testUILanguageNegotiation() { // No selected language is available. \Drupal::config('language.negotiation')->set('selected_langcode', $langcode_unknown)->save(); $test = array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_SELECTED), + 'language_negotiation' => array(LanguageNegotiationSelected::METHOD_ID), 'path' => 'admin/config', 'expect' => $default_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED, + 'expected_method_id' => LanguageNegotiationSelected::METHOD_ID, 'http_header' => $http_header_browser_fallback, 'message' => 'SELECTED > DEFAULT: UI language is switched based on selected language.', ); @@ -185,46 +186,46 @@ function testUILanguageNegotiation() { $tests = array( // Default, browser preference should have no influence. array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_SELECTED), + 'language_negotiation' => array(LanguageNegotiationUrl::METHOD_ID, LanguageNegotiationSelected::METHOD_ID), 'path' => 'admin/config', 'expect' => $default_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED, + 'expected_method_id' => LanguageNegotiationSelected::METHOD_ID, 'http_header' => $http_header_browser_fallback, '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' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_SELECTED), + 'language_negotiation' => array(LanguageNegotiationUrl::METHOD_ID, LanguageNegotiationSelected::METHOD_ID), 'path' => "$langcode/admin/config", 'expect' => $language_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_URL, + 'expected_method_id' => LanguageNegotiationUrl::METHOD_ID, 'http_header' => $http_header_browser_fallback, 'message' => 'URL (PATH) > DEFAULT: with language prefix, UI language is switched based on path prefix', ), // Default, go by browser preference. array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_BROWSER), + 'language_negotiation' => array(LanguageNegotiationUrl::METHOD_ID, LanguageNegotiationBrowser::METHOD_ID), 'path' => 'admin/config', 'expect' => $language_browser_fallback_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_BROWSER, + 'expected_method_id' => LanguageNegotiationBrowser::METHOD_ID, 'http_header' => $http_header_browser_fallback, 'message' => 'URL (PATH) > BROWSER: no language prefix, UI language is determined by browser language preference', ), // Prefix, switch to the language. array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_BROWSER), + 'language_negotiation' => array(LanguageNegotiationUrl::METHOD_ID, LanguageNegotiationBrowser::METHOD_ID), 'path' => "$langcode/admin/config", 'expect' => $language_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_URL, + 'expected_method_id' => LanguageNegotiationUrl::METHOD_ID, 'http_header' => $http_header_browser_fallback, 'message' => 'URL (PATH) > BROWSER: with language prefix, UI language is based on path prefix', ), // Default, browser language preference is not one of site's lang. array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_BROWSER, LANGUAGE_NEGOTIATION_SELECTED), + 'language_negotiation' => array(LanguageNegotiationUrl::METHOD_ID, LanguageNegotiationBrowser::METHOD_ID, LanguageNegotiationSelected::METHOD_ID), 'path' => 'admin/config', 'expect' => $default_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED, + 'expected_method_id' => LanguageNegotiationSelected::METHOD_ID, 'http_header' => $http_header_blah, 'message' => 'URL (PATH) > BROWSER > DEFAULT: no language prefix and browser language preference set to unknown language should use default language', ), @@ -235,7 +236,8 @@ function testUILanguageNegotiation() { } // Unknown language prefix should return 404. - variable_set('language_negotiation_' . Language::TYPE_INTERFACE, language_language_negotiation_info()); + $definitions = \Drupal::languageManager()->getNegotiator()->getNegotiationMethods(); + variable_set('language_negotiation_' . Language::TYPE_INTERFACE, array_flip(array_keys($definitions))); $this->drupalGet("$langcode_unknown/admin/config", array(), $http_header_browser_fallback); $this->assertResponse(404, "Unknown language path prefix should return 404"); @@ -245,10 +247,10 @@ function testUILanguageNegotiation() { $account->save(); $test = array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER, LANGUAGE_NEGOTIATION_SELECTED), + 'language_negotiation' => array(LanguageNegotiationUser::METHOD_ID, LanguageNegotiationSelected::METHOD_ID), 'path' => 'admin/config', 'expect' => $default_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED, + 'expected_method_id' => LanguageNegotiationSelected::METHOD_ID, 'http_header' => array(), 'message' => 'USER > DEFAULT: no preferred user language setting, the UI language is default', ); @@ -260,10 +262,10 @@ function testUILanguageNegotiation() { $account->save(); $test = array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER, LANGUAGE_NEGOTIATION_SELECTED), + 'language_negotiation' => array(LanguageNegotiationUser::METHOD_ID, LanguageNegotiationSelected::METHOD_ID), 'path' => 'admin/config', 'expect' => $default_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED, + 'expected_method_id' => LanguageNegotiationSelected::METHOD_ID, 'http_header' => array(), 'message' => 'USER > DEFAULT: invalid preferred user language setting, the UI language is default', ); @@ -274,10 +276,10 @@ function testUILanguageNegotiation() { $account->save(); $test = array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER, LANGUAGE_NEGOTIATION_SELECTED), + 'language_negotiation' => array(LanguageNegotiationUser::METHOD_ID, LanguageNegotiationSelected::METHOD_ID), 'path' => 'admin/config', 'expect' => $language_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_USER, + 'expected_method_id' => LanguageNegotiationUser::METHOD_ID, 'http_header' => array(), 'message' => 'USER > DEFAULT: defined prefereed user language setting, the UI language is based on user setting', ); @@ -288,10 +290,10 @@ function testUILanguageNegotiation() { $account->save(); $test = array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER_ADMIN, LANGUAGE_NEGOTIATION_SELECTED), + 'language_negotiation' => array(LanguageNegotiationUserAdmin::METHOD_ID, LanguageNegotiationSelected::METHOD_ID), 'path' => 'admin/config', 'expect' => $default_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED, + 'expected_method_id' => LanguageNegotiationSelected::METHOD_ID, 'http_header' => array(), 'message' => 'USER ADMIN > DEFAULT: no preferred user admin language setting, the UI language is default', ); @@ -302,10 +304,10 @@ function testUILanguageNegotiation() { $account->save(); $test = array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER_ADMIN, LANGUAGE_NEGOTIATION_SELECTED), + 'language_negotiation' => array(LanguageNegotiationUserAdmin::METHOD_ID, LanguageNegotiationSelected::METHOD_ID), 'path' => 'admin/config', 'expect' => $default_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED, + 'expected_method_id' => LanguageNegotiationSelected::METHOD_ID, 'http_header' => array(), 'message' => 'USER ADMIN > DEFAULT: invalid preferred user admin language setting, the UI language is default', ); @@ -316,56 +318,20 @@ function testUILanguageNegotiation() { $account->save(); $test = array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER_ADMIN, LANGUAGE_NEGOTIATION_SELECTED), + 'language_negotiation' => array(LanguageNegotiationUserAdmin::METHOD_ID, LanguageNegotiationSelected::METHOD_ID), 'path' => 'admin/config', 'expect' => $language_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_USER_ADMIN, + 'expected_method_id' => LanguageNegotiationUserAdmin::METHOD_ID, 'http_header' => array(), 'message' => 'USER ADMIN > DEFAULT: defined prefereed user admin language setting, the UI language is based on user setting', ); $this->runTest($test); - - // Setup for domain negotiation, first configure the language to have domain - // URL. - $edit = array("domain[$langcode]" => $language_domain); - $this->drupalPostForm("admin/config/regional/language/detection/url", $edit, t('Save configuration')); - // Set the site to use domain language negotiation. - - $tests = array( - // Default domain, browser preference should have no influence. - array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_SELECTED), - 'language_negotiation_url_part' => LANGUAGE_NEGOTIATION_URL_DOMAIN, - 'path' => 'admin/config', - 'expect' => $default_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED, - 'http_header' => $http_header_browser_fallback, - 'message' => 'URL (DOMAIN) > DEFAULT: default domain should get default language', - ), - // Language domain specific URL, we set the 'HTTP_HOST' property of - // \Drupal::request()->server in \Drupal\language_test\LanguageTestManager - // to simulate this. - array( - 'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_SELECTED), - 'language_negotiation_url_part' => LANGUAGE_NEGOTIATION_URL_DOMAIN, - 'language_test_domain' => $language_domain . ':88', - 'path' => 'admin/config', - 'expect' => $language_string, - 'expected_method_id' => LANGUAGE_NEGOTIATION_URL, - 'http_header' => $http_header_browser_fallback, - 'message' => 'URL (DOMAIN) > DEFAULT: domain example.cn should switch to Chinese', - ), - ); - - foreach ($tests as $test) { - $this->runTest($test); - } } protected function runTest($test) { if (!empty($test['language_negotiation'])) { $method_weights = array_flip($test['language_negotiation']); - language_negotiation_set(Language::TYPE_INTERFACE, $method_weights); + $this->container->get('language_negotiator')->saveConfiguration(Language::TYPE_INTERFACE, $method_weights); } if (!empty($test['language_negotiation_url_part'])) { \Drupal::config('language.negotiation') @@ -449,7 +415,7 @@ function testLanguageDomain() { // Change the domain for the Italian language. $edit = array( - 'language_negotiation_url_part' => LANGUAGE_NEGOTIATION_URL_DOMAIN, + 'language_negotiation_url_part' => LanguageNegotiationUrl::CONFIG_DOMAIN, 'domain[it]' => 'it.example.com', ); $this->drupalPostForm('admin/config/regional/language/detection/url', $edit, t('Save configuration')); diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php index 07ee9a9..847348e 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php @@ -7,6 +7,7 @@ namespace Drupal\language\Tests; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl; use Drupal\simpletest\WebTestBase; use Symfony\Component\HttpFoundation\Request; @@ -46,8 +47,6 @@ function setUp() { $edit = array('language_interface[enabled][language-url]' => 1); $this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings')); - // Reset static caching. - drupal_static_reset('language_list'); } /** @@ -105,7 +104,7 @@ private function checkUrl($language, $message1, $message2) { function testDomainNameNegotiationPort() { $language_domain = 'example.fr'; $edit = array( - 'language_negotiation_url_part' => LANGUAGE_NEGOTIATION_URL_DOMAIN, + 'language_negotiation_url_part' => LanguageNegotiationUrl::CONFIG_DOMAIN, 'domain[fr]' => $language_domain ); $this->drupalPostForm('admin/config/regional/language/detection/url', $edit, t('Save configuration')); @@ -115,11 +114,11 @@ function testDomainNameNegotiationPort() { // Enable domain configuration. \Drupal::config('language.negotiation') - ->set('url.source', LANGUAGE_NEGOTIATION_URL_DOMAIN) + ->set('url.source', LanguageNegotiationUrl::CONFIG_DOMAIN) ->save(); // Reset static caching. - drupal_static_reset('language_list'); + $this->container->get('language_manager')->reset(); // In case index.php is part of the URLs, we need to adapt the asserted // URLs as well. diff --git a/core/modules/language/tests/Drupal/language/Tests/LanguageNegotiationUrlTest.php b/core/modules/language/tests/Drupal/language/Tests/LanguageNegotiationUrlTest.php new file mode 100644 index 0000000..48a39a1 --- /dev/null +++ b/core/modules/language/tests/Drupal/language/Tests/LanguageNegotiationUrlTest.php @@ -0,0 +1,165 @@ + 'Language negotiation URL', + 'description' => 'Tests the URL/domain Language negotiation plugin', + 'group' => 'Language', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + + // Set up some languages to be used by the language-based path processor. + $languages = array( + 'de' => (object) array( + 'id' => 'de', + ), + 'en' => (object) array( + 'id' => 'en', + ), + ); + + // Create a language manager stub. + $language_manager = $this->getMockBuilder('Drupal\language\ConfigurableLanguageManagerInterface') + ->getMock(); + $language_manager->expects($this->any()) + ->method('getCurrentLanguage') + ->will($this->returnValue($languages['en'])); + $language_manager->expects($this->any()) + ->method('getLanguages') + ->will($this->returnValue($languages)); + $this->languageManager = $language_manager; + + // Create a user stub. + $this->user = $this->getMockBuilder('Drupal\Core\Session\AccountInterface') + ->getMock(); + } + + /** + * Test domain language negotiation. + * + * @dataProvider providerTestDomain + */ + public function testDomain($http_host, $domains, $expected_langcode) { + $config_data = array( + 'source' => LanguageNegotiationUrl::CONFIG_DOMAIN, + 'domains' => $domains, + ); + + $config_object = $this->getMockBuilder('Drupal\Core\Config\Config') + ->disableOriginalConstructor() + ->getMock(); + $config_object->expects($this->any()) + ->method('get') + ->with('url') + ->will($this->returnValue($config_data)); + + $config = $this->getMockBuilder('Drupal\Core\Config\ConfigFactory') + ->disableOriginalConstructor() + ->getMock(); + $config->expects($this->any()) + ->method('get') + ->with('language.negotiation') + ->will($this->returnValue($config_object)); + + $request = Request::create('', 'GET', array(), array(), array(), array('HTTP_HOST' => $http_host)); + $method = new LanguageNegotiationUrl(); + $method->setLanguageManager($this->languageManager); + $method->setConfig($config); + $method->setCurrentUser($this->user); + $this->assertEquals($expected_langcode, $method->getLangcode($request)); + } + + /** + * Provides data for the domain test. + * + * @return array + * An array of data for checking domain negotation. + */ + public function providerTestDomain() { + + $domain_configuration[] = array( + 'http_host' => 'example.de', + 'domains' => array( + 'de' => 'http://example.de', + ), + 'expected_langocde' => 'de', + ); + // No configuration. + $domain_configuration[] = array( + 'http_host' => 'example.de', + 'domains' => array(), + 'expected_langocde' => FALSE, + ); + // HTTP host with a port. + $domain_configuration[] = array( + 'http_host' => 'example.de:8080', + 'domains' => array( + 'de' => 'http://example.de', + ), + 'expected_langocde' => 'de', + ); + // Domain configuration with https://. + $domain_configuration[] = array( + 'http_host' => 'example.de', + 'domains' => array( + 'de' => 'https://example.de', + ), + 'expected_langocde' => 'de', + ); + // Non-matching HTTP host. + $domain_configuration[] = array( + 'http_host' => 'example.com', + 'domains' => array( + 'de' => 'http://example.com', + ), + 'expected_langocde' => 'de', + ); + // Testing a non-existing language. + $domain_configuration[] = array( + 'http_host' => 'example.com', + 'domains' => array( + 'it' => 'http://example.it', + ), + 'expected_langocde' => FALSE, + ); + // Multiple domain configurations. + $domain_configuration[] = array( + 'http_host' => 'example.com', + 'domains' => array( + 'de' => 'http://example.de', + 'en' => 'http://example.com', + ), + 'expected_langocde' => 'en', + ); + return $domain_configuration; + } +} diff --git a/core/modules/language/tests/language_test/language_test.module b/core/modules/language/tests/language_test/language_test.module index 8e8cbaf..4b8f932 100644 --- a/core/modules/language/tests/language_test/language_test.module +++ b/core/modules/language/tests/language_test/language_test.module @@ -5,10 +5,8 @@ * Mock module for language layer tests. */ -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\HttpKernelInterface; - use Drupal\Core\Language\Language; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUI; /** * Implements hook_page_build(). @@ -47,47 +45,20 @@ function language_test_language_types_info_alter(array &$language_types) { unset($language_types[Language::TYPE_CONTENT]['fixed']); // By default languages are not configurable. Make Language::TYPE_CONTENT // configurable. - $configurable = \Drupal::config('system.language.types')->get('configurable'); + $configurable = \Drupal::config('language.types')->get('configurable'); if (!in_array(Language::TYPE_CONTENT, $configurable)) { $configurable[] = Language::TYPE_CONTENT; - \Drupal::config('system.language.types')->set('configurable', $configurable)->save(); + \Drupal::config('language.types')->set('configurable', $configurable)->save(); } } } /** - * Implements hook_language_negotiation_info(). - */ -function language_test_language_negotiation_info() { - if (\Drupal::state()->get('language_test.language_negotiation_info')) { - $info = array( - 'callbacks' => array( - 'negotiation' => 'language_test_language_negotiation_method', - ), - 'file' => drupal_get_path('module', 'language_test') .'/language_test.module', - 'weight' => -10, - 'description' => t('This is a test language negotiation method.'), - ); - - return array( - 'test_language_negotiation_method' => array( - 'name' => t('Test'), - 'types' => array(Language::TYPE_CONTENT, 'test_language_type', 'fixed_test_language_type'), - ) + $info, - 'test_language_negotiation_method_ts' => array( - 'name' => t('Type-specific test'), - 'types' => array('test_language_type'), - ) + $info, - ); - } -} - -/** * Implements hook_language_negotiation_info_alter(). */ function language_test_language_negotiation_info_alter(array &$negotiation_info) { if (\Drupal::state()->get('language_test.language_negotiation_info_alter')) { - unset($negotiation_info[LANGUAGE_NEGOTIATION_INTERFACE]); + unset($negotiation_info[LanguageNegotiationUI::METHOD_ID]); } } @@ -96,21 +67,13 @@ function language_test_language_negotiation_info_alter(array &$negotiation_info) */ function language_test_store_language_negotiation() { $last = array(); - print_r(language_types_get_all()); - foreach (language_types_get_all() as $type) { + foreach (\Drupal::languageManager()->getDefinedLanguageTypes() as $type) { $last[$type] = language($type)->id; } \Drupal::state()->set('language_test.language_negotiation_last', $last); } /** - * Provides a test language negotiation method. - */ -function language_test_language_negotiation_method($languages) { - return 'it'; -} - -/** * Implements hook_language_fallback_candidates_alter(). */ function language_test_language_fallback_candidates_alter(array &$candidates, array $context) { diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestManager.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestManager.php deleted file mode 100644 index 3c460b6..0000000 --- a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestManager.php +++ /dev/null @@ -1,28 +0,0 @@ -get('language_test.domain')) { - \Drupal::request()->server->set('HTTP_HOST', $test_domain); - } - return parent::init(); - } - -} diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestServiceProvider.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestServiceProvider.php deleted file mode 100644 index 39aef04..0000000 --- a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestServiceProvider.php +++ /dev/null @@ -1,28 +0,0 @@ -getDefinition('language_manager'); - $definition->setClass('Drupal\language_test\LanguageTestManager'); - } - -} - diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTest.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTest.php new file mode 100644 index 0000000..a726c5c --- /dev/null +++ b/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTest.php @@ -0,0 +1,38 @@ +translateFilterValues(); $langcode = $filter_values['langcode']; - drupal_static_reset('language_list'); + $this->languageManager->reset(); $languages = language_list(); $langname = isset($langcode) ? $languages[$langcode]->name : "- None -"; diff --git a/core/modules/locale/lib/Drupal/locale/Form/TranslateFormBase.php b/core/modules/locale/lib/Drupal/locale/Form/TranslateFormBase.php index ea4c9ce..c5447e0 100644 --- a/core/modules/locale/lib/Drupal/locale/Form/TranslateFormBase.php +++ b/core/modules/locale/lib/Drupal/locale/Form/TranslateFormBase.php @@ -161,7 +161,7 @@ protected function translateFilters() { $filters = array(); // Get all languages, except English. - drupal_static_reset('language_list'); + $this->languageManager->reset(); $languages = language_list(); $language_options = array(); foreach ($languages as $langcode => $language) { @@ -171,7 +171,7 @@ protected function translateFilters() { } // Pick the current interface language code for the filter. - $default_langcode = $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id; + $default_langcode = $this->languageManager->getCurrentLanguage()->id; if (!isset($language_options[$default_langcode])) { $available_langcodes = array_keys($language_options); $default_langcode = array_shift($available_langcodes); diff --git a/core/modules/locale/lib/Drupal/locale/LocaleConfigSubscriber.php b/core/modules/locale/lib/Drupal/locale/LocaleConfigSubscriber.php index 11fa899..4da6e6c 100644 --- a/core/modules/locale/lib/Drupal/locale/LocaleConfigSubscriber.php +++ b/core/modules/locale/lib/Drupal/locale/LocaleConfigSubscriber.php @@ -12,7 +12,7 @@ use Drupal\Core\Config\ConfigEvent; use Drupal\Core\Config\StorageDispatcher; use Drupal\Core\Language\Language; -use Drupal\Core\Language\LanguageManager; +use Drupal\Core\Language\LanguageManagerInterface; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Event\GetResponseEvent; @@ -29,7 +29,7 @@ class LocaleConfigSubscriber implements EventSubscriberInterface { /** * The language manager. * - * @var \Drupal\Core\Language\LanguageManager + * @var \Drupal\Core\Language\LanguageManagerInterface */ protected $languageManager; @@ -48,7 +48,7 @@ class LocaleConfigSubscriber implements EventSubscriberInterface { * @param \Drupal\Core\Config\Context\ConfigContext $config_context * The configuration context service. */ - public function __construct(LanguageManager $language_manager, ContextInterface $config_context) { + public function __construct(LanguageManagerInterface $language_manager, ContextInterface $config_context) { $this->languageManager = $language_manager; $this->defaultConfigContext = $config_context; } @@ -72,7 +72,7 @@ public function configContext(ConfigEvent $event) { elseif ($account = $context->get('user.account')) { $context->set('locale.language', language_load($account->getPreferredLangcode())); } - elseif ($language = $this->languageManager->getLanguage(Language::TYPE_INTERFACE)) { + elseif ($language = $this->languageManager->getCurrentLanguage()) { $context->set('locale.language', $language); } } diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php index 87febe4..1c2c597 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php @@ -47,7 +47,6 @@ function testMachineNameLTR() { $edit = array(); $edit['predefined_langcode'] = 'ar'; $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language')); - drupal_static_reset('language_list'); $edit = array( 'site_default_language' => 'ar', @@ -84,7 +83,6 @@ function testContentTypeLanguageConfiguration() { 'direction' => '0', ); $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language')); - drupal_static_reset('language_list'); // Set the content type to use multilingual support. $this->drupalGet("admin/structure/types/manage/{$type2->type}"); @@ -95,7 +93,7 @@ function testContentTypeLanguageConfiguration() { $this->drupalPostForm("admin/structure/types/manage/{$type2->type}", $edit, t('Save content type')); $this->assertRaw(t('The content type %type has been updated.', array('%type' => $type2->name))); $this->drupalLogout(); - drupal_static_reset('language_list'); + \Drupal::languageManager()->reset(); // Verify language selection is not present on the node add form. $this->drupalLogin($web_user); @@ -152,13 +150,12 @@ function testContentTypeDirLang() { $edit = array(); $edit['predefined_langcode'] = 'ar'; $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language')); - drupal_static_reset('language_list'); + \Drupal::languageManager()->reset(); // Install Spanish language. $edit = array(); $edit['predefined_langcode'] = 'es'; $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language')); - drupal_static_reset('language_list'); // Set the content type to use multilingual support. $this->drupalGet("admin/structure/types/manage/{$type->type}"); @@ -222,7 +219,6 @@ function testNodeAdminLanguageFilter() { // Enable multiple languages. $this->drupalPostForm('admin/config/regional/language/edit/en', array('locale_translate_english' => TRUE), t('Save language')); $this->drupalPostForm('admin/config/regional/language/add', array('predefined_langcode' => 'zh-hant'), t('Add language')); - drupal_static_reset('language_list'); // Create two nodes: English and Chinese. $node_en = $this->drupalCreateNode(array('langcode' => 'en')); diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php index d02f763..a9ce77f 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php @@ -223,7 +223,7 @@ function testJavaScriptTranslation() { 'direction' => '0', ); $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language')); - drupal_static_reset('language_list'); + $this->container->get('language_manager')->reset(); // Build the JavaScript translation file. diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php index cd8ec73..97d2d3e 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php @@ -7,9 +7,13 @@ namespace Drupal\locale\Tests; -use Drupal\simpletest\WebTestBase; -use Drupal\Core\Language\Language; use Drupal\Component\Utility\String; +use Drupal\Core\Language\Language; +use Drupal\Core\Language\LanguageManager; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSelected; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl; +use Drupal\simpletest\WebTestBase; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; /** * Locale uninstall with English UI functional test. @@ -90,14 +94,18 @@ function testUninstallProcess() { // Change language negotiation options. drupal_load('module', 'locale'); - \Drupal::config('system.language.types')->set('configurable', language_types_get_default() + array('language_custom' => TRUE))->save(); - variable_set('language_negotiation_' . Language::TYPE_INTERFACE, language_language_negotiation_info()); - variable_set('language_negotiation_' . Language::TYPE_CONTENT, language_language_negotiation_info()); - variable_set('language_negotiation_' . Language::TYPE_URL, language_language_negotiation_info()); + // Pick only core language types. + $language_manager = new LanguageManager(); + $default_types = $language_manager->getLanguageTypes(); + \Drupal::config('language.types')->set('configurable', $default_types + array('language_custom' => TRUE))->save(); + $config = array_flip(array_keys(\Drupal::service('plugin.manager.language_negotiation_method')->getDefinitions())); + variable_set('language_negotiation_' . Language::TYPE_INTERFACE, $config); + variable_set('language_negotiation_' . Language::TYPE_CONTENT, $config); + variable_set('language_negotiation_' . Language::TYPE_URL, $config); // Change language negotiation settings. \Drupal::config('language.negotiation') - ->set('url.source', LANGUAGE_NEGOTIATION_URL_PREFIX) + ->set('url.source', LanguageNegotiationUrl::CONFIG_PATH_PREFIX) ->set('session.parameter', TRUE) ->save(); @@ -113,19 +121,16 @@ function testUninstallProcess() { // Check JavaScript files deletion. $this->assertTrue($result = !file_exists($js_file), String::format('JavaScript file deleted: %file', array('%file' => $result ? $js_file : 'found'))); - // Check language count. - $language_count = $this->container->get('state')->get('language_count') ?: 1; - $this->assertEqual($language_count, 1, String::format('Language count: %count', array('%count' => $language_count))); - // Check language negotiation. - require_once DRUPAL_ROOT . '/core/includes/language.inc'; - $this->assertTrue(count(language_types_get_all()) == count(language_types_get_default()), 'Language types reset'); - $language_negotiation = language_negotiation_method_get_first(Language::TYPE_INTERFACE) == LANGUAGE_NEGOTIATION_SELECTED; - $this->assertTrue($language_negotiation, String::format('Interface language negotiation: %setting', array('%setting' => $language_negotiation ? 'none' : 'set'))); - $language_negotiation = language_negotiation_method_get_first(Language::TYPE_CONTENT) == LANGUAGE_NEGOTIATION_SELECTED; - $this->assertTrue($language_negotiation, String::format('Content language negotiation: %setting', array('%setting' => $language_negotiation ? 'none' : 'set'))); - $language_negotiation = language_negotiation_method_get_first(Language::TYPE_URL) == LANGUAGE_NEGOTIATION_SELECTED; - $this->assertTrue($language_negotiation, String::format('URL language negotiation: %setting', array('%setting' => $language_negotiation ? 'none' : 'set'))); + try { + $message = 'Language negotiation is not avilable.'; + $this->assertTrue(count($this->container->get('language_manager')->getLanguageTypes()) == count($default_types), 'Language types reset'); + \Drupal::service('language_negotiator'); + $this->fail($message); + } + catch (InvalidArgumentException $e) { + $this->pass($message); + } // Check language negotiation method settings. $this->assertFalse(\Drupal::config('language.negotiation')->get('url.source'), 'URL language negotiation method indicator settings cleared.'); diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php index 64e9333..8ae5a8e 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php @@ -80,7 +80,7 @@ protected function setTranslationsDirectory($path) { protected function addLanguage($langcode) { $edit = array('predefined_langcode' => $langcode); $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language')); - drupal_static_reset('language_list'); + $this->container->get('language_manager')->reset(); $this->assertTrue(language_load($langcode), String::format('Language %langcode added.', array('%langcode' => $langcode))); } diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc index 5256244..eb0dfe4 100644 --- a/core/modules/locale/locale.bulk.inc +++ b/core/modules/locale/locale.bulk.inc @@ -21,7 +21,7 @@ * @deprecated Use \Drupal\locale\Form\LocaleForm::import() */ function locale_translate_import_form($form, &$form_state) { - drupal_static_reset('language_list'); + Drupal::languageManager()->reset(); $languages = language_list(); // Initialize a language list to the ones available, including English if we diff --git a/core/modules/locale/locale.install b/core/modules/locale/locale.install index 5c98791..96446b1 100644 --- a/core/modules/locale/locale.install +++ b/core/modules/locale/locale.install @@ -6,6 +6,7 @@ */ use Drupal\Core\Language\Language; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSelected; /** * Implements hook_install(). @@ -500,20 +501,10 @@ function locale_update_8004() { $types = update_variable_get('language_types', NULL); if (!empty($types)) { foreach ($types as $type => $configurable) { - // Rename the negotiation and language switch callback keys. + // Change the structure of the language negotiation configuration. $negotiation = update_variable_get('language_negotiation_' . $type, NULL); if (!empty($negotiation)) { - foreach ($negotiation as &$method) { - if (isset($method['callbacks']['language'])) { - $method['callbacks']['negotiation'] = $method['callbacks']['language']; - unset($method['callbacks']['language']); - } - if (isset($method['callbacks']['switcher'])) { - $method['callbacks']['language_switch'] = $method['callbacks']['switcher']; - unset($method['callbacks']['switcher']); - } - } - update_variable_set('language_negotiation_' . $type, $negotiation); + update_variable_set('language_negotiation_' . $type, array_keys($negotiation)); } // Rename the language negotiation methods weight variable. @@ -646,16 +637,11 @@ function locale_update_8005() { } /** - * Convert language_negotiation_* variables to use the new callbacks. + * Convert language_negotiation_* variables. * * @ingroup config_upgrade */ function locale_update_8007() { - $variable_names = array( - 'language_negotiation_language_interface', - 'language_negotiation_language_content', - 'language_negotiation_language_url', - ); // Add all language type weight variables. As the function language_types() // is not available its functionality is rebuild. $language_types = update_variable_get('language_types', array( @@ -664,20 +650,9 @@ function locale_update_8007() { Language::TYPE_URL => FALSE, )); foreach ($language_types as $language_type => $configurable) { - $variable_names[] = 'language_negotiation_methods_weight_' . $language_type; + $variable_names['language_negotiation_' . $language_type] = 'language_negotiation_methods_weight_' . $language_type; } - $callback_map = array( - 'locale_language_from_url' => 'language_from_url', - 'locale_language_switcher_url' => 'language_switcher_url', - 'locale_language_url_rewrite_url' => 'language_url_rewrite_url', - 'locale_language_from_session' => 'language_from_session', - 'locale_language_switcher_session' => 'language_switcher_session', - 'locale_language_url_rewrite_session' => 'language_url_rewrite_session', - 'locale_language_from_user' => 'language_from_user', - 'locale_language_from_browser' => 'language_from_browser', - 'locale_language_url_fallback' => 'language_url_fallback', - 'locale_language_from_interface' => 'language_from_interface', - ); + $type_map = array( 'locale-interface' => 'language-interface', 'locale-url' => 'language-url', @@ -686,37 +661,24 @@ function locale_update_8007() { 'locale-user' => 'language-user', 'locale-session' => 'language-session', ); - foreach ($variable_names as $variable_name) { - $value = update_variable_get($variable_name); + foreach ($variable_names as $variable_name => $weight_variable_name) { + $value = update_variable_get($weight_variable_name); // Skip processing if the variable is not stored in the db. if ($value === NULL) { + update_variable_del($variable_name); continue; } - $new_value = $value; - foreach ($value as $type => $type_settings) { - // Convert the file. - if (isset($type_settings['file']) && (strpos($type_settings['file'], 'core/includes/locale.inc') !== FALSE)) { - $new_value[$type]['file'] = 'core/modules/language/language.negotiation.inc'; - } - // Convert the callbacks. - if (is_array($type_settings) && isset($type_settings['callbacks'])) { - foreach ($type_settings['callbacks'] as $key => $callback) { - if (isset($callback_map[$callback])) { - $new_value[$type]['callbacks'][$key] = $callback_map[$callback]; - } - } - } + $new_value = array(); + foreach ($value as $type => $weight) { // Convert the type. if (isset($type_map[$type])) { - $new_value[$type_map[$type]] = $new_value[$type]; - unset($new_value[$type]); + $type = $type_map[$type]; } + $new_value[$type] = $weight; } - // If necessary maintain the order of the values / keys of the variable. - if (stristr($variable_name, 'language_negotiation_methods_weight_') !== FALSE) { - asort($new_value); - } + asort($new_value); update_variable_set($variable_name, $new_value); + update_variable_del($weight_variable_name); } } @@ -845,25 +807,11 @@ function locale_update_8011() { * Renames language_default language negotiation method to language_selected. */ function locale_update_8013() { - // @todo We only need language.inc here because LANGUAGE_NEGOTIATION_SELECTED - // is defined there. Remove this line once that has been converted to a class - // constant. - require_once DRUPAL_ROOT . '/core/includes/language.inc'; - $weight = update_variable_get('language_negotiation_methods_weight_language_interface', NULL); + $weight = update_variable_get('language_negotiation_language_interface', NULL); if ($weight !== NULL) { - $weight[LANGUAGE_NEGOTIATION_SELECTED] = $weight['language-default']; + $weight[LanguageNegotiationSelected::METHOD_ID] = $weight['language-default']; unset($weight['language-default']); - update_variable_set('language_negotiation_methods_weight_language_interface', $weight); - } - - $negotiation_interface = update_variable_get('language_negotiation_language_interface', NULL); - if ($negotiation_interface !== NULL) { - if (isset($negotiation_interface['language-default'])) { - $negotiation_interface[LANGUAGE_NEGOTIATION_SELECTED] = $negotiation_interface['language-default']; - $negotiation_interface[LANGUAGE_NEGOTIATION_SELECTED]['callbacks']['negotiation'] = 'language_from_selected'; - unset($negotiation_interface['language-default']); - update_variable_set('language_negotiation_language_interface', $negotiation_interface); - } + update_variable_set('language_negotiation_language_interface', $weight); } } diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml index 568f21d..3eaad0d 100644 --- a/core/modules/locale/locale.services.yml +++ b/core/modules/locale/locale.services.yml @@ -1,8 +1,6 @@ services: locale_config_subscriber: class: Drupal\locale\LocaleConfigSubscriber - tags: - - { name: event_subscriber } arguments: ['@language_manager', '@config.context'] paramconverter.configentity_admin: class: Drupal\locale\ParamConverter\LocaleAdminPathConfigEntityConverter diff --git a/core/modules/node/lib/Drupal/node/Controller/NodeController.php b/core/modules/node/lib/Drupal/node/Controller/NodeController.php index a60696c..126332f 100644 --- a/core/modules/node/lib/Drupal/node/Controller/NodeController.php +++ b/core/modules/node/lib/Drupal/node/Controller/NodeController.php @@ -69,7 +69,7 @@ public function add(NodeTypeInterface $node_type) { 'uid' => $account->id(), 'name' => $account->getUsername() ?: '', 'type' => $node_type->type, - 'langcode' => $langcode ? $langcode : $this->languageManager()->getLanguage()->id, + 'langcode' => $langcode ? $langcode : $this->languageManager()->getCurrentLanguage()->id, )); $form = $this->entityManager()->getForm($node); diff --git a/core/modules/node/lib/Drupal/node/Entity/Node.php b/core/modules/node/lib/Drupal/node/Entity/Node.php index fee4c18..a3fa6d1 100644 --- a/core/modules/node/lib/Drupal/node/Entity/Node.php +++ b/core/modules/node/lib/Drupal/node/Entity/Node.php @@ -199,7 +199,7 @@ public function prepareLangcode() { // Load languages the node exists in. $node_translations = $this->getTranslationLanguages(); // Load the language from content negotiation. - $content_negotiation_langcode = \Drupal::languageManager()->getLanguage(Language::TYPE_CONTENT)->id; + $content_negotiation_langcode = \Drupal::languageManager()->getCurrentLanguage(Language::TYPE_CONTENT)->id; // If there is a translation available, use it. if (isset($node_translations[$content_negotiation_langcode])) { $langcode = $content_negotiation_langcode; diff --git a/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php b/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php index 9efe421..c9b54c5 100644 --- a/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php +++ b/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php @@ -159,7 +159,7 @@ public function alterQuery($query, array $tables, $op, AccountInterface $account $subquery->condition('na.grant_' . $op, 1, '>='); // Add langcode-based filtering if this is a multilingual site. - if (language_multilingual()) { + if (\Drupal::languageManager()->isMultilingual()) { // If no specific langcode to check for is given, use the grant entry // which is set as a fallback. // If a specific langcode is given, use the grant entry for it. diff --git a/core/modules/node/lib/Drupal/node/NodeListController.php b/core/modules/node/lib/Drupal/node/NodeListController.php index 8d488ca..b9582ae 100644 --- a/core/modules/node/lib/Drupal/node/NodeListController.php +++ b/core/modules/node/lib/Drupal/node/NodeListController.php @@ -81,7 +81,7 @@ public function buildHeader() { 'class' => array(RESPONSIVE_PRIORITY_LOW), ), ); - if (language_multilingual()) { + if (\Drupal::languageManager()->isMultilingual()) { $header['language_name'] = array( 'data' => $this->t('Language'), 'class' => array(RESPONSIVE_PRIORITY_LOW), @@ -114,8 +114,9 @@ public function buildRow(EntityInterface $entity) { ); $row['status'] = $entity->isPublished() ? $this->t('published') : $this->t('not published'); $row['changed'] = $this->dateService->format($entity->getChangedTime(), 'short'); - if (language_multilingual()) { - $row['language_name'] = language_name($langcode); + $language_manager = \Drupal::languageManager(); + if ($language_manager->isMultilingual()) { + $row['language_name'] = $language_manager->getLanguageName($langcode); } $row['operations']['data'] = $this->buildOperations($entity); return $row + parent::buildRow($entity); diff --git a/core/modules/node/lib/Drupal/node/NodeViewBuilder.php b/core/modules/node/lib/Drupal/node/NodeViewBuilder.php index eda9b4c..fcaeaed 100644 --- a/core/modules/node/lib/Drupal/node/NodeViewBuilder.php +++ b/core/modules/node/lib/Drupal/node/NodeViewBuilder.php @@ -50,7 +50,7 @@ public function buildContent(array $entities, array $displays, $view_mode, $lang $entity->content['langcode'] = array( '#type' => 'item', '#title' => t('Language'), - '#markup' => language_name($langcode), + '#markup' => $this->languageManager->getLanguageName($langcode), '#prefix' => '
', '#suffix' => '
' ); diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php b/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php index f535182..67b34e7 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php @@ -7,6 +7,7 @@ namespace Drupal\node\Tests; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl; use Drupal\simpletest\WebTestBase; use Drupal\Core\Language\Language; @@ -99,7 +100,7 @@ function testMultilingualNodeForm() { $this->assertTrue($node->language()->id == $langcode && $node->body->value == $body_value, 'Field language correctly changed.'); // Enable content language URL detection. - language_negotiation_set(Language::TYPE_CONTENT, array(LANGUAGE_NEGOTIATION_URL => 0)); + $this->container->get('language_negotiator')->saveConfiguration(Language::TYPE_CONTENT, array(LanguageNegotiationUrl::METHOD_ID => 0)); // Test multilingual field language fallback logic. $this->drupalGet("it/node/{$node->id()}"); diff --git a/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php b/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php index 4f94f43..0653743 100644 --- a/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php +++ b/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php @@ -104,7 +104,7 @@ function testAliasTranslation() { $this->container->get('path.alias_manager')->cacheClear(); // Languages are cached on many levels, and we need to clear those caches. - drupal_static_reset('language_list'); + $this->container->get('language_manager')->reset(); $this->rebuildContainer(); $languages = language_list(); @@ -117,7 +117,10 @@ function testAliasTranslation() { $this->drupalGet('fr/' . $edit['path[alias]']); $this->assertText($french_node->body->value, 'Alias for French translation works.'); - // Confirm that the alias is returned by url(). + // Confirm that the alias is returned by url(). Languages are cached on + // many levels, and we need to clear those caches. + $this->container->get('language_manager')->reset(); + $languages = language_list(); $url = $this->container->get('url_generator')->generateFromPath('node/' . $french_node->id(), array('language' => $languages['fr'])); $this->assertTrue(strpos($url, $edit['path[alias]']), 'URL contains the path alias.'); diff --git a/core/modules/path/path.admin.inc b/core/modules/path/path.admin.inc index b2941a0..6574208 100644 --- a/core/modules/path/path.admin.inc +++ b/core/modules/path/path.admin.inc @@ -57,7 +57,7 @@ function path_admin_overview($keys = NULL) { 'attributes' => array('title' => $data->source), )); if ($multilanguage) { - $row['data']['language_name'] = language_name($data->langcode); + $row['data']['language_name'] = \Drupal::languageManager()->getLanguageName($data->langcode); } $operations = array(); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index 9e7b6b2..925dea8 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -19,6 +19,7 @@ use Drupal\Core\Language\Language; use Drupal\Core\StreamWrapper\PublicStream; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\DependencyInjection\Reference; /** * Base class for Drupal tests. @@ -968,8 +969,11 @@ protected function prepareEnvironment() { // Reset and create a new service container. $this->container = new ContainerBuilder(); - // @todo Remove this once this class has no calls to t() and format_plural() - $this->container->register('string_translation', 'Drupal\Core\StringTranslation\TranslationManager'); + + // @todo Remove this once this class has no calls to t() and format_plural() + $this->container->register('language_manager', 'Drupal\Core\Language\LanguageManager'); + $this->container->register('string_translation', 'Drupal\Core\StringTranslation\TranslationManager') + ->addArgument(new Reference('language_manager')); // Register info parser. $this->container->register('info_parser', 'Drupal\Core\Extension\InfoParser'); diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml index 9d585f0..9acf83d 100644 --- a/core/modules/system/config/schema/system.schema.yml +++ b/core/modules/system/config/schema/system.schema.yml @@ -175,23 +175,6 @@ system.filter: - type: string label: 'Protocol' -system.language.types: - type: mapping - label: 'Language types' - mapping: - all: - type: sequence - label: 'All language types' - sequence: - - type: string - label: 'Language type' - configurable: - type: sequence - label: 'Configurable language types' - sequence: - - type: string - label: 'Language type' - system.logging: type: mapping label: 'Logging settings' diff --git a/core/modules/system/language.api.php b/core/modules/system/language.api.php index baafb7a..8684406 100644 --- a/core/modules/system/language.api.php +++ b/core/modules/system/language.api.php @@ -35,123 +35,6 @@ function hook_language_switch_links_alter(array &$links, $type, $path) { } /** - * Define language types. - * - * @return - * An associative array of language type definitions. The keys are the - * identifiers, which are also used as names for global variables representing - * the types in the bootstrap phase. The values are associative arrays that - * may contain the following elements: - * - name: The human-readable language type identifier. - * - description: A description of the language type. - * - locked: A boolean indicating if the user can choose wether to configure - * the language type or not using the UI. - * - fixed: A fixed array of language negotiation method identifiers to use to - * initialize this language. If locked is set to TRUE and fixed is set, it - * will always use the specified methods in the given priority order. If not - * present and locked is TRUE then LANGUAGE_NEGOTIATION_INTERFACE will be - * used. - * - * @todo Rename the 'fixed' key to something more meaningful, for instance - * 'negotiation settings'. - * - * @see hook_language_types_info_alter() - * @ingroup language_negotiation - */ -function hook_language_types_info() { - return array( - 'custom_language_type' => array( - 'name' => t('Custom language'), - 'description' => t('A custom language type.'), - 'locked' => FALSE, - ), - 'fixed_custom_language_type' => array( - 'locked' => TRUE, - 'fixed' => array('custom_language_negotiation_method'), - ), - ); -} - -/** - * Perform alterations on language types. - * - * @param $language_types - * Array of language type definitions. - * - * @see hook_language_types_info() - * @ingroup language_negotiation - */ -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.'); - } -} - -/** - * Define language negotiation methods. - * - * @return - * An associative array of language negotiation method definitions. The keys - * are method identifiers, and the values are associative arrays defining - * each method, with the following elements: - * - types: An array of allowed language types. If a language negotiation - * method does not specify which language types it should be used with, it - * will be available for all the configurable language types. - * - callbacks: An associative array of functions that will be called to - * perform various tasks. Possible elements are: - * - negotiation: (required) Name of the callback function that determines - * the language value. - * - language_switch: (optional) Name of the callback function that - * determines links for a language switcher block associated with this - * method. See language_switcher_url() for an example. - * - url_rewrite: (optional) Name of the callback function that provides URL - * rewriting, if needed by this method. - * - file: The file where callback functions are defined (this file will be - * included before the callbacks are invoked). - * - weight: The default weight of the method. - * - name: The translated human-readable name for the method. - * - description: A translated longer description of the method. - * - config: An internal path pointing to the method's configuration page. - * - cache: The value Drupal's page cache should be set to for the current - * method to be invoked. - * - * @see hook_language_negotiation_info_alter() - * @ingroup language_negotiation - */ -function hook_language_negotiation_info() { - return array( - 'custom_language_negotiation_method' => array( - 'callbacks' => array( - 'negotiation' => 'custom_negotiation_callback', - 'language_switch' => 'custom_language_switch_callback', - 'url_rewrite' => 'custom_url_rewrite_callback', - ), - 'file' => drupal_get_path('module', 'custom') . '/custom.module', - 'weight' => -4, - 'types' => array('custom_language_type'), - 'name' => t('Custom language negotiation method'), - 'description' => t('This is a custom language negotiation method.'), - 'cache' => 0, - ), - ); -} - -/** - * Perform alterations on language negotiation methods. - * - * @param $negotiation_info - * Array of language negotiation method definitions. - * - * @see hook_language_negotiation_info() - * @ingroup language_negotiation - */ -function hook_language_negotiation_info_alter(array &$negotiation_info) { - if (isset($negotiation_info['custom_language_method'])) { - $negotiation_info['custom_language_method']['config'] = 'admin/config/regional/language/detection/custom-language-method'; - } -} - -/** * @} End of "addtogroup hooks". */ diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php index 373c90b..663ef4c 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php @@ -15,9 +15,7 @@ /** * Tests the entity access controller. */ -class EntityAccessTest extends EntityUnitTestBase { - - public static $modules = array('language', 'locale'); +class EntityAccessTest extends EntityLanguageTestBase { public static function getInfo() { return array( @@ -29,16 +27,7 @@ public static function getInfo() { function setUp() { parent::setUp(); - $this->installSchema('system', array('variable', 'url_alias')); - $this->installConfig(array('language')); - - // Create the default languages. - $default_language = language_save(language_default()); - $languages = language_default_locked_languages($default_language->weight); - foreach ($languages as $language) { - language_save($language); - } - + $this->installSchema('system', 'url_alias'); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php index dbf4f9c..ee1c0d0 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php @@ -15,6 +15,18 @@ */ abstract class EntityLanguageTestBase extends EntityUnitTestBase { + /** + * The language manager service. + * + * @var \Drupal\Core\Language\LanguageManagerInterface + */ + protected $languageManager; + + /** + * The available language codes. + * + * @var array + */ protected $langcodes; /** @@ -36,6 +48,8 @@ function setUp() { parent::setUp(); + $this->languageManager = $this->container->get('language_manager'); + $this->installSchema('system', 'variable'); $this->installSchema('entity_test', array( 'entity_test_mul', @@ -92,8 +106,8 @@ function setUp() { } // Create the default languages. - $default_language = language_save(language_default()); - $languages = language_default_locked_languages($default_language->weight); + $default_language = language_save($this->languageManager->getDefaultLanguage()); + $languages = $this->languageManager->getDefaultLockedLanguages($default_language->weight); foreach ($languages as $language) { language_save($language); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php index a084ebf..eb234b4 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php @@ -446,7 +446,7 @@ function testEntityTranslationAPI() { * Tests language fallback applied to field and entity translations. */ function testLanguageFallback() { - $current_langcode = $this->container->get('language_manager')->getLanguage(Language::TYPE_CONTENT)->id; + $current_langcode = $this->languageManager->getCurrentLanguage(Language::TYPE_CONTENT)->id; $this->langcodes[] = $current_langcode; $values = array(); @@ -473,7 +473,7 @@ function testLanguageFallback() { $this->assertEqual($translation->language()->id, $default_langcode, 'The current translation language matches the expected one.'); // Check that language fallback respects language weight by default. - $languages = language_list(); + $languages = $this->languageManager->getLanguages(); $languages[$langcode]->weight = -1; language_save($languages[$langcode]); $translation = $this->entityManager->getTranslationFromContext($entity, $langcode2); diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorLanguageTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorLanguageTest.php index 10de2a8..2e734ef 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorLanguageTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorLanguageTest.php @@ -69,6 +69,7 @@ public function setUp() { $custom_strings[$definition['label']] = $langcode . ' ' . $definition['label']; } $this->addCustomTranslations($langcode, array('' => $custom_strings)); + $this->rebuildContainer(); } // Write test settings.php with new translations. $this->writeCustomTranslations(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php index 48d1e1f..8ce7d1a 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php @@ -84,7 +84,7 @@ protected function setUp() { $this->drupalPostForm('admin/config/regional/settings', $edit, t('Save configuration')); // Reset the static cache of the language list. - drupal_static_reset('language_list'); + $this->container->get('language_manager')->reset(); // Check that lolspeak is the default language for the site. $this->assertEqual(language_default()->id, 'xx', 'Lolspeak is the default language'); @@ -251,6 +251,7 @@ protected function installLanguages() { drupal_unlink($filename); } } + $this->container->get('language_manager')->reset(); } /** diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 34d8dde..ec0a10a 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -2252,7 +2252,7 @@ function system_update_8058() { * @ingroup config_upgrade */ function system_update_8059() { - update_variables_to_config('system.language.types', array( + update_variables_to_config('language.types', array( 'language_interface' => 'configurable.language_interface', 'language_content' => 'configurable.language_content', 'language_url' => 'configurable.language_content', diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module index f6eaa41..a0f7bf5 100644 --- a/core/modules/toolbar/toolbar.module +++ b/core/modules/toolbar/toolbar.module @@ -700,7 +700,7 @@ function toolbar_user_role_update(RoleInterface $role) { * A unique cache ID for the user. */ function _toolbar_get_user_cid($uid) { - return 'toolbar_' . $uid . ':' . \Drupal::languageManager()->getLanguage(Language::TYPE_INTERFACE)->id; + return 'toolbar_' . $uid . ':' . \Drupal::languageManager()->getCurrentLanguage()->id; } /** diff --git a/core/modules/user/lib/Drupal/user/AccountFormController.php b/core/modules/user/lib/Drupal/user/AccountFormController.php index 4983b0f..7c62df0 100644 --- a/core/modules/user/lib/Drupal/user/AccountFormController.php +++ b/core/modules/user/lib/Drupal/user/AccountFormController.php @@ -10,7 +10,10 @@ use Drupal\Core\Entity\ContentEntityFormController; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Language\Language; -use Drupal\Core\Language\LanguageManager; +use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\language\ConfigurableLanguageManagerInterface; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUser; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -21,7 +24,7 @@ /** * The language manager. * - * @var \Drupal\Core\Language\LanguageManager + * @var \Drupal\Core\Language\LanguageManagerInterface */ protected $languageManager; @@ -30,10 +33,10 @@ * * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager. - * @param \Drupal\Core\Language\LanguageManager $language_manager + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. */ - public function __construct(EntityManagerInterface $entity_manager, LanguageManager $language_manager) { + public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager) { parent::__construct($entity_manager); $this->languageManager = $language_manager; } @@ -214,9 +217,12 @@ public function form(array $form, array &$form_state) { $user_preferred_admin_langcode = $register ? $language_interface->id : $account->getPreferredAdminLangcode(); - // Is default the interface language? - include_once DRUPAL_ROOT . '/core/includes/language.inc'; - $interface_language_is_default = language_negotiation_method_get_first(Language::TYPE_INTERFACE) != LANGUAGE_NEGOTIATION_SELECTED; + // Is the user preferred language enabled? + $user_language_enabled = FALSE; + if ($this->languageManager instanceof ConfigurableLanguageManagerInterface) { + $negotiator = $this->languageManager->getNegotiator(); + $user_language_enabled = $negotiator && $negotiator->isNegotiationMethodEnabled(LanguageNegotiationUser::METHOD_ID, Language::TYPE_INTERFACE); + } $form['language'] = array( '#type' => $this->languageManager->isMultilingual() ? 'details' : 'container', '#title' => $this->t('Language settings'), @@ -230,26 +236,22 @@ public function form(array $form, array &$form_state) { '#title' => $this->t('Site language'), '#languages' => Language::STATE_CONFIGURABLE, '#default_value' => $user_preferred_langcode, - '#description' => $interface_language_is_default ? $this->t("This account's preferred language for e-mails and site presentation.") : $this->t("This account's preferred language for e-mails."), + '#description' => $user_language_enabled ? $this->t("This account's preferred language for e-mails and site presentation.") : $this->t("This account's preferred language for e-mails."), ); // Only show the account setting for Administration pages language to users // if one of the detection and selection methods uses it. $show_admin_language = FALSE; - if ($this->moduleHandler->moduleExists('language') && $this->languageManager->isMultilingual()) { - foreach (language_types_info() as $type_key => $language_type) { - $negotiation_settings = variable_get("language_negotiation_{$type_key}", array()); - if ($show_admin_language = isset($negotiation_settings[LANGUAGE_NEGOTIATION_USER_ADMIN])) { - break; - } - } + if ($account->hasPermission('access administration pages') && $this->languageManager instanceof ConfigurableLanguageManagerInterface) { + $negotiator = $this->languageManager->getNegotiator(); + $show_admin_language = $negotiator && $negotiator->isNegotiationMethodEnabled(LanguageNegotiationUserAdmin::METHOD_ID); } $form['language']['preferred_admin_langcode'] = array( '#type' => 'language_select', '#title' => $this->t('Administration pages language'), '#languages' => Language::STATE_CONFIGURABLE, '#default_value' => $user_preferred_admin_langcode, - '#access' => $show_admin_language && user_access('access administration pages', $account), + '#access' => $show_admin_language, ); // User entities contain both a langcode property (for identifying the // language of the entity data) and a preferred_langcode property (see diff --git a/core/modules/user/lib/Drupal/user/Form/UserPasswordForm.php b/core/modules/user/lib/Drupal/user/Form/UserPasswordForm.php index 07d35e9..ebc7f5f 100644 --- a/core/modules/user/lib/Drupal/user/Form/UserPasswordForm.php +++ b/core/modules/user/lib/Drupal/user/Form/UserPasswordForm.php @@ -127,7 +127,7 @@ public function validateForm(array &$form, array &$form_state) { * {@inheritdoc} */ public function submitForm(array &$form, array &$form_state) { - $langcode = $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id; + $langcode = $this->languageManager->getCurrentLanguage()->id; $account = $form_state['values']['account']; // Mail one time login URL and instructions using current language. diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php index 7c643cf..494016a 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php @@ -157,7 +157,7 @@ public function initDisplay(ViewExecutable $view, array &$display, array &$optio $skip_cache = \Drupal::config('views.settings')->get('skip_cache'); if (empty($view->editing) || !$skip_cache) { - $cid = 'unpackOptions:' . hash('sha256', serialize(array($this->options, $options))) . ':' . \Drupal::languageManager()->getLanguage()->id; + $cid = 'unpackOptions:' . hash('sha256', serialize(array($this->options, $options))) . ':' . \Drupal::languageManager()->getCurrentLanguage()->id; if (empty(static::$unpackOptions[$cid])) { $cache = \Drupal::cache('views_info')->get($cid); if (!empty($cache->data)) { diff --git a/core/modules/views/lib/Drupal/views/ViewsData.php b/core/modules/views/lib/Drupal/views/ViewsData.php index 988c3c8..c297c03 100644 --- a/core/modules/views/lib/Drupal/views/ViewsData.php +++ b/core/modules/views/lib/Drupal/views/ViewsData.php @@ -11,7 +11,7 @@ use Drupal\Core\Config\ConfigFactory; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\Language; -use Drupal\Core\Language\LanguageManager; +use Drupal\Core\Language\LanguageManagerInterface; /** * Class to manage and lazy load cached views data. @@ -75,7 +75,7 @@ class ViewsData { /** * The language manager * - * @var \Drupal\Core\Language\LanguageManager + * @var \Drupal\Core\Language\LanguageManagerInterface */ protected $languageManager; @@ -88,15 +88,15 @@ class ViewsData { * The configuration factory object to use. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler class to use for invoking hooks. - * @param \Drupal\Core\Language\LanguageManager $language_manager + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. */ - public function __construct(CacheBackendInterface $cache_backend, ConfigFactory $config, ModuleHandlerInterface $module_handler, LanguageManager $language_manager) { + public function __construct(CacheBackendInterface $cache_backend, ConfigFactory $config, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager) { $this->cacheBackend = $cache_backend; $this->moduleHandler = $module_handler; $this->languageManager = $language_manager; - $this->langcode = $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id; + $this->langcode = $this->languageManager->getCurrentLanguage()->id; $this->skipCache = $config->get('views.settings')->get('skip_cache'); } diff --git a/core/modules/views/tests/Drupal/views/Tests/ViewsDataTest.php b/core/modules/views/tests/Drupal/views/Tests/ViewsDataTest.php index b6e6880..1850903 100644 --- a/core/modules/views/tests/Drupal/views/Tests/ViewsDataTest.php +++ b/core/modules/views/tests/Drupal/views/Tests/ViewsDataTest.php @@ -76,11 +76,9 @@ protected function setUp() { $configs['views.settings']['skip_cache'] = FALSE; $this->configFactory = $this->getConfigFactoryStub($configs); $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); - $this->languageManager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager') - ->disableOriginalConstructor() - ->getMock(); + $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $this->languageManager->expects($this->any()) - ->method('getLanguage') + ->method('getCurrentLanguage') ->will($this->returnValue(new Language(array('id' => 'en')))); $this->viewsData = new ViewsData($this->cacheBackend, $this->configFactory, $this->moduleHandler, $this->languageManager); diff --git a/core/tests/Drupal/Tests/Core/Datetime/DateTest.php b/core/tests/Drupal/Tests/Core/Datetime/DateTest.php index 32d2103..d856b2c 100644 --- a/core/tests/Drupal/Tests/Core/Datetime/DateTest.php +++ b/core/tests/Drupal/Tests/Core/Datetime/DateTest.php @@ -60,9 +60,7 @@ public static function getInfo() { protected function setUp() { $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); - $this->languageManager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager') - ->disableOriginalConstructor() - ->getMock(); + $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $this->stringTranslation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface'); $this->date = new Date($this->entityManager, $this->languageManager, $this->stringTranslation); diff --git a/core/tests/Drupal/Tests/Core/Menu/ContextualLinkManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/ContextualLinkManagerTest.php index fa27a3e..0e52b9f 100644 --- a/core/tests/Drupal/Tests/Core/Menu/ContextualLinkManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/ContextualLinkManagerTest.php @@ -113,11 +113,9 @@ protected function setUp() { $property->setAccessible(TRUE); $property->setValue($this->contextualLinkManager, $this->accessManager); - $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager') - ->disableOriginalConstructor() - ->getMock(); + $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $language_manager->expects($this->any()) - ->method('getLanguage') + ->method('getCurrentLanguage') ->will($this->returnValue(new Language(array('id' => 'en')))); $this->moduleHandler = $this->getMock('\Drupal\Core\Extension\ModuleHandlerInterface'); diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php index 47384ac..aaaa625 100644 --- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php @@ -102,11 +102,9 @@ protected function getLocalTaskManager($module_dirs, $route_name, $route_params) $property->setAccessible(TRUE); $property->setValue($manager, $factory); - $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager') - ->disableOriginalConstructor() - ->getMock(); + $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $language_manager->expects($this->any()) - ->method('getLanguage') + ->method('getCurrentLanguage') ->will($this->returnValue(new Language(array('id' => 'en')))); $cache_backend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php index da5f90d..6fc7c1c 100644 --- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php @@ -265,11 +265,9 @@ protected function setupLocalTaskManager() { $property->setAccessible(TRUE); $property->setValue($this->manager, $this->factory); - $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager') - ->disableOriginalConstructor() - ->getMock(); + $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $language_manager->expects($this->any()) - ->method('getLanguage') + ->method('getCurrentLanguage') ->will($this->returnValue(new Language(array('id' => 'en')))); $this->manager->setCacheBackend($this->cacheBackend, $language_manager, 'local_task', array('local_task' => 1)); diff --git a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php index f49184f..9197085 100644 --- a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php +++ b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php @@ -8,17 +8,21 @@ namespace Drupal\Tests\Core\PathProcessor; use Drupal\Component\Utility\Settings; +use Drupal\Core\Language\Language; use Drupal\Core\PathProcessor\PathProcessorAlias; use Drupal\Core\PathProcessor\PathProcessorDecode; use Drupal\Core\PathProcessor\PathProcessorFront; use Drupal\Core\PathProcessor\PathProcessorManager; use Drupal\language\HttpKernel\PathProcessorLanguage; +use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl; use Symfony\Component\HttpFoundation\Request; use Drupal\Tests\UnitTestCase; /** * Tests path processor functionality. + * + * @group PathApi */ class PathProcessorTest extends UnitTestCase { @@ -45,12 +49,44 @@ public function setUp() { } $this->languages = $languages; + // Create a stub configuration. + $language_prefixes = array_keys($this->languages); + $config = array( + 'url' => array( + 'prefixes' => array_combine($language_prefixes, $language_prefixes) + ) + ); + + // Create a URL-based language negotiation method definition. + $method_definitions = array( + LanguageNegotiationUrl::METHOD_ID => array( + 'class' => '\Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl', + ), + ); + + // Create a URL-based language negotiation method. + $method_instance = new LanguageNegotiationUrl($config); + // Create a language manager stub. - $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager'); + $language_manager = $this->getMockBuilder('Drupal\language\ConfigurableLanguageManagerInterface') + ->getMock(); $language_manager->expects($this->any()) - ->method('getLanguage') + ->method('getCurrentLanguage') ->will($this->returnValue($languages['en'])); + $language_manager->expects($this->any()) + ->method('getLanguages') + ->will($this->returnValue($this->languages)); + $language_manager->expects($this->any()) + ->method('getLanguageTypes') + ->will($this->returnValue(array(Language::TYPE_INTERFACE))); + $language_manager->expects($this->any()) + ->method('getNegotiationMethods') + ->will($this->returnValue($method_definitions)); + $language_manager->expects($this->any()) + ->method('getNegotiationMethodInstance') + ->will($this->returnValue($method_instance)); + $method_instance->setLanguageManager($language_manager); $this->languageManager = $language_manager; } @@ -79,23 +115,41 @@ function testProcessInbound() { // Create a stub config factory with all config settings that will be checked // during this test. - $language_prefixes = array_keys($this->languages); $config_factory_stub = $this->getConfigFactoryStub( array( 'system.site' => array( 'page.front' => 'user' ), 'language.negotiation' => array( - 'url.prefixes' => array_combine($language_prefixes, $language_prefixes) - ) + 'url' => array( + 'prefixes' => array('fr' => 'fr'), + ), + ), ) ); + // Create a language negotiator stub. + $negotiator = $this->getMockBuilder('Drupal\language\LanguageNegotiatorInterface') + ->getMock(); + $negotiator->expects($this->any()) + ->method('getNegotiationMethods') + ->will($this->returnValue(array(LanguageNegotiationUrl::METHOD_ID => array('class' => 'Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl')))); + $method = new LanguageNegotiationUrl(); + $method->setConfig($config_factory_stub); + $method->setLanguageManager($this->languageManager); + $negotiator->expects($this->any()) + ->method('getNegotiationMethodInstance') + ->will($this->returnValue($method)); + + // Create a user stub. + $current_user = $this->getMockBuilder('Drupal\Core\Session\AccountInterface') + ->getMock(); + // Create the processors. $alias_processor = new PathProcessorAlias($alias_manager); $decode_processor = new PathProcessorDecode(); $front_processor = new PathProcessorFront($config_factory_stub); - $language_processor = new PathProcessorLanguage($config_factory_stub, new Settings(array()), $this->languageManager, $this->languages); + $language_processor = new PathProcessorLanguage($config_factory_stub, new Settings(array()), $this->languageManager, $negotiator, $current_user); // First, test the processor manager with the processors in the incorrect // order. The alias processor will run before the language processor, meaning diff --git a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php index 3e86b66..ec84708 100644 --- a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php @@ -113,9 +113,9 @@ public function testDefaultPluginManagerWithEmptyCache() { ->with($cid . ':en', $this->expectedDefinitions); $language = new Language(array('id' => 'en')); - $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager'); + $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $language_manager->expects($this->once()) - ->method('getLanguage') + ->method('getCurrentLanguage') ->with(Language::TYPE_INTERFACE) ->will($this->returnValue($language)); @@ -144,9 +144,9 @@ public function testDefaultPluginManagerWithFilledCache() { ->method('set'); $language = new Language(array('id' => 'en')); - $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager'); + $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $language_manager->expects($this->once()) - ->method('getLanguage') + ->method('getCurrentLanguage') ->with(Language::TYPE_INTERFACE) ->will($this->returnValue($language)); @@ -173,9 +173,9 @@ public function testCacheClearWithTags() { ->method('deleteMultiple'); $language = new Language(array('id' => 'en')); - $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager'); + $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $language_manager->expects($this->once()) - ->method('getLanguage') + ->method('getCurrentLanguage') ->with(Language::TYPE_INTERFACE) ->will($this->returnValue($language)); diff --git a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php index 6b7334f..469b0e3 100644 --- a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php @@ -79,7 +79,7 @@ protected function setUp() { $this->urlGenerator = $this->getMock('\Drupal\Core\Routing\UrlGenerator', array(), array(), '', FALSE); $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); - $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManager'); + $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $this->linkGenerator = new LinkGenerator($this->urlGenerator, $this->moduleHandler, $this->languageManager); } @@ -89,7 +89,7 @@ protected function setUp() { */ public function setUpLanguageManager() { $this->languageManager->expects($this->any()) - ->method('getLanguage') + ->method('getCurrentLanguage') ->will($this->returnValue(new Language(array('id' => 'en')))); }