Change record status: 
Project: 
Introduced in branch: 
8.x
Introduced in version: 
8.0
Description: 

Motivation for this change

Previously, since only the parts strictly needed to objectify lower-level subsystems were converted to OOP, the language system was an unsettling mixture of object-oriented and procedural code. This change objectifies the language system by moving most procedural code and constants to regular classes. It removes language.inc, language.negotiation.inc and the related explicit includes, together with the code living in bootstrap.inc. Several functions are deprecated.

Changes introduced in this patch

API changes

  • hook_language_negotiation_info() and the related procedural callbacks are converted to plugins.
  • The language_count state entry was removed, as getting the language list from config and counting them has the same performance of getting the language count from state. This allows to remove all the synchronization code.
  • Most functions in the language system are moved to the Language manager or the Language negotiation services:
    bootstrap.inc
    language() LanguageManagerInterface::getCurrentLanguage()
    language_default() LanguageManagerInterface::getDefaultLanguage()
    language_list() LanguageManagerInterface::getLanguages()
    language_load() LanguageManagerInterface::getLanguage()
    language_default_locked_languages() LanguageManagerInterface::getDefaultLockedLanguages()
    language_types_get_default() LanguageManagerInterface::getLanguageTypes()
    language_name() LanguageManagerInterface::getLanguageName()
    language_is_locked() LanguageManagerInterface::isLanguageLocked()
    language.inc
    language_negotiation_get_switch_links() LanguageManagerInterface::getLanguageSwitchLinks()
    language_types_info() ConfigurableLanguageManagerInterface::getDefinedLanguageTypesInfo()
    language_types_get_configurable() ConfigurableLanguageManagerInterface::getLanguageTypes()
    language_types_disable() Removed, no actual use case for this.
    language_update_locked_weights() ConfigurableLanguageManagerInterface::updateLockedLanguageWeights()
    language_types_get_all() ConfigurableLanguageManagerInterface::getDefinedLanguageTypes()
    language_types_set() LanguageNegotiatorInterface::updateConfiguration()
    language_types_initialize() LanguageNegotiatorInterface::initializeType()
    language_negotiation_method_get_first() LanguageNegotiatorInterface::getPrimaryNegotiationMethod()
    language_negotiation_method_enabled() LanguageNegotiatorInterface::isNegotiationMethodEnabled()
    language_negotiation_purge() LanguageNegotiatorInterface::purgeConfiguration()
    language_negotiation_set() LanguageNegotiatorInterface::saveConfiguration()
    language_negotiation_info() LanguageNegotiatorInterface::getNegotiationMethods()
    language_negotiation_method_invoke() LanguageNegotiator::negotiateLanguage() (protected)
    language_url_split_prefix() LanguageNegotiationFoo::processInbound()
    language.negotiation.inc
    language_from_selected() LanguageNegotiationSelected::getLangcode()
    language_from_browser() LanguageNegotiationBrowser::getLangcode()
    language_from_user() LanguageNegotiationUser::getLangcode()
    language_from_user_admin() LanguageNegotiationUserAdmin::getLangcode()
    language_from_session() LanguageNegotiationSession::getLangcode()
    language_from_url() LanguageNegotiationFoo::getLangcode()
    language_url_fallback() LanguageNegotiationFooFallback::getLangcode()
    language_switcher_session() LanguageNegotiationSession::getLanguageSwitchLinks()
    language_switcher_url() LanguageNegotiationFoo::getLanguageSwitchLinks()
    language_url_rewrite_session() LanguageNegotiationSession::processOutbound()

New Objects

  • \Drupal\Component\Utility\UserAgent
  • \Drupal\language\ConfigurableLanguageManager
  • \Drupal\language\EventSubscriber\LanguageRequestSubscriber
  • \Drupal\language\LanguageNegotiationMethodBase
  • \Drupal\language\LanguageNegotiationMethodManager
  • \Drupal\language\LanguageNegotiator

New Interfaces

  • \Drupal\Core\Language\LanguageManagerInterface
  • \Drupal\language\ConfigurableLanguageManagerInterface
  • \Drupal\language\LanguageNegotiationMethodInterface
  • \Drupal\language\LanguageNegotiatorInterface
  • \Drupal\language\LanguageSwitcherInterface

New Plugins

  • \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser
  • \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSelected
  • \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSession
  • \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUI
  • \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationFoo
  • \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationFooFallback
  • \Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUser
  • \Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin

New Services

  • \Drupal\language\LanguageServiceProvider
  • plugin.manager.language_negotiation_method
  • language_negotiator

Deprecated functions in bootstrap.inc

language()
\Drupal::languageManager()->getCurrentLanguage()
language_list()
\Drupal::languageManager()->getLanguages()
language_load()
\Drupal::languageManager()->getLanguage()
language_default()
\Drupal::languageManager()->getDefaultLanguage()

How to define language negotiation methods

Previously hook hook_language_negotiation_info() could be used to define language negotiation methods. The most relevant key was 'callbacks' that allowed to define procedural callbacks for language detection, url rewriting and language switching, where the last two were optional. Now a language negotiation method is defined through the plugin system by defining an annotated class, which can implement four different interfaces:

  • \Drupal\language\LanguageNegotiationMethodInterface (required)
  • \Drupal\language\LanguageSwitcherInterface
  • \Drupal\Core\PathProcessor\InboundPathProcessorInterface
  • \Drupal\Core\PathProcessor\OutboundPathProcessorInterface

If the plugin implements the last three interfaces, it will have language switching and url rewriting (inbound/outbound) capabilities respectively:

D7:

function foo_language_negotiation_info() {
  return array(
    'foo_method' => array(
      'callbacks' => array(
        'language' => 'foo_language_negotiation_callback',
        'switcher' => 'foo_language_switcher_callback',
        'url_rewrite' => 'foo_language_url_rewrite_callback',
      ),
      'file' => drupal_get_path('module', 'foo') . '/foo.module',
      'weight' => -4,
      'types' => array(LANGUAGE_TYPE_INTERFACE, LANGUAGE_TYPE_CONTENT, LANGUAGE_TYPE_URL),
      'name' => t('Foo language negotiation method'),
      'description' => t('This is a custom language negotiation method.'),
      'cache' => 0,
      'config' => 'admin/config/regional/language/detection/foo',
    ),
  );
}

D8:


namespace Drupal\foo\Plugin\LanguageNegotiation;

use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
use Drupal\language\LanguageNegotiationMethodInterface;
use Drupal\language\LanguageSwitcherInterface;

/**
 * Foo language negotiation method.
 *
 * @Plugin(
 *   id = \Drupal\foo\Plugin\LanguageNegotiation\LanguageNegotiationFoo::METHOD_ID,
 *   types = {\Drupal\Core\Language\Language::TYPE_INTERFACE, \Drupal\Core\Language\Language::TYPE_CONTENT, \Drupal\Core\Language\Language::TYPE_URL},
 *   weight = -4,
 *   name = @Translation("Foo language negotiation method"),
 *   description = @Translation("This is a custom language negotiation method."),
 *   config_path = "admin/config/regional/language/detection/foo"
 * )
 */
class LanguageNegotiationFoo implements LanguageNegotiationMethodInterface, InboundPathProcessorInterface, OutboundPathProcessorInterface, LanguageSwitcherInterface {

  /**
   * The language negotiation method id.
   */
  const METHOD_ID = 'language-foo';

  /**
   * {@inheritdoc}
   */
  public function getLangcode(Request $request = NULL) {
    $langcode = NULL;

    // Do stuff

    return $langcode;
  }

  /**
   * {@inheritdoc}
   */
  public function processInbound($path, Request $request) {
    // Do stuff

    return $path;
  }

  /**
   * {@inheritdoc}
   */
  public function processOutbound($path, &$options = array(), Request $request = NULL) {
    // Do stuff

    return $path;
  }

  /**
   * {@inheritdoc}
   */
  function getLanguageSwitchLinks(Request $request, $type, $path) {
    // Do stuff

    return $links;
  }

}

Language negotiation method definitions are still alterable through hook_language_negotiation_info_alter().

Impacts: 
Module developers