first commit
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Annotation;
|
||||
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
|
||||
/**
|
||||
* Defines a language negotiation annotation object.
|
||||
*
|
||||
* Plugin Namespace: Plugin\LanguageNegotiation
|
||||
*
|
||||
* For a working example, see
|
||||
* \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser.
|
||||
*
|
||||
* @see \Drupal\language\LanguageNegotiator
|
||||
* @see \Drupal\language\LanguageNegotiationMethodManager
|
||||
* @see \Drupal\language\LanguageNegotiationMethodInterface
|
||||
* @see hook_language_negotiation_info_alter()
|
||||
* @see plugin_api
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class LanguageNegotiation extends Plugin {
|
||||
|
||||
/**
|
||||
* The language negotiation plugin ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* An array of allowed language types.
|
||||
*
|
||||
* If a language negotiation plugin does not specify which language types it
|
||||
* should be used with, it will be available for all the configurable
|
||||
* language types.
|
||||
*
|
||||
* @var string[]
|
||||
* An array of language types, such as the
|
||||
* \Drupal\Core\Language\LanguageInterface::TYPE_* constants.
|
||||
*/
|
||||
public $types;
|
||||
|
||||
/**
|
||||
* The default weight of the language negotiation plugin.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $weight;
|
||||
|
||||
/**
|
||||
* The human-readable name of the language negotiation plugin.
|
||||
*
|
||||
* @ingroup plugin_translatable
|
||||
*
|
||||
* @var \Drupal\Core\Annotation\Translation
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* The description of the language negotiation plugin.
|
||||
*
|
||||
* @ingroup plugin_translatable
|
||||
*
|
||||
* @var \Drupal\Core\Annotation\Translation
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* The route pointing to the plugin's configuration page.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $config_route_name;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Config;
|
||||
|
||||
/**
|
||||
* Provides a common trait for working with language override collection names.
|
||||
*/
|
||||
trait LanguageConfigCollectionNameTrait {
|
||||
|
||||
/**
|
||||
* Creates a configuration collection name based on a language code.
|
||||
*
|
||||
* @param string $langcode
|
||||
* The language code.
|
||||
*
|
||||
* @return string
|
||||
* The configuration collection name for a language code.
|
||||
*/
|
||||
protected function createConfigCollectionName($langcode) {
|
||||
return 'language.' . $langcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a configuration collection name to a language code.
|
||||
*
|
||||
* @param string $collection
|
||||
* The configuration collection name.
|
||||
*
|
||||
* @return string
|
||||
* The language code of the collection.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Exception thrown if the provided collection name is not in the format
|
||||
* "language.LANGCODE".
|
||||
*
|
||||
* @see self::createConfigCollectionName()
|
||||
*/
|
||||
protected function getLangcodeFromCollectionName($collection) {
|
||||
preg_match('/^language\.(.*)$/', $collection, $matches);
|
||||
if (!isset($matches[1])) {
|
||||
throw new \InvalidArgumentException("'$collection' is not a valid language override collection");
|
||||
}
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Config;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Config\ConfigCollectionInfo;
|
||||
use Drupal\Core\Config\ConfigCrudEvent;
|
||||
use Drupal\Core\Config\ConfigFactoryOverrideBase;
|
||||
use Drupal\Core\Config\ConfigRenameEvent;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\Core\Config\TypedConfigManagerInterface;
|
||||
use Drupal\Core\Language\LanguageDefault;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Provides language overrides for the configuration factory.
|
||||
*/
|
||||
class LanguageConfigFactoryOverride extends ConfigFactoryOverrideBase implements LanguageConfigFactoryOverrideInterface, EventSubscriberInterface {
|
||||
|
||||
use LanguageConfigCollectionNameTrait;
|
||||
|
||||
/**
|
||||
* The configuration storage.
|
||||
*
|
||||
* Do not access this directly. Should be accessed through self::getStorage()
|
||||
* so that the cache of storages per langcode is used.
|
||||
*
|
||||
* @var \Drupal\Core\Config\StorageInterface
|
||||
*/
|
||||
protected $baseStorage;
|
||||
|
||||
/**
|
||||
* An array of configuration storages keyed by langcode.
|
||||
*
|
||||
* @var \Drupal\Core\Config\StorageInterface[]
|
||||
*/
|
||||
protected $storages;
|
||||
|
||||
/**
|
||||
* The typed config manager.
|
||||
*
|
||||
* @var \Drupal\Core\Config\TypedConfigManagerInterface
|
||||
*/
|
||||
protected $typedConfigManager;
|
||||
|
||||
/**
|
||||
* An event dispatcher instance to use for configuration events.
|
||||
*
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* The language object used to override configuration data.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageInterface
|
||||
*/
|
||||
protected $language;
|
||||
|
||||
/**
|
||||
* Constructs the LanguageConfigFactoryOverride object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\StorageInterface $storage
|
||||
* The configuration storage engine.
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
|
||||
* An event dispatcher instance to use for configuration events.
|
||||
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
|
||||
* The typed configuration manager.
|
||||
* @param \Drupal\Core\Language\LanguageDefault $default_language
|
||||
* The default language.
|
||||
*/
|
||||
public function __construct(StorageInterface $storage, EventDispatcherInterface $event_dispatcher, TypedConfigManagerInterface $typed_config, LanguageDefault $default_language) {
|
||||
$this->baseStorage = $storage;
|
||||
$this->eventDispatcher = $event_dispatcher;
|
||||
$this->typedConfigManager = $typed_config;
|
||||
// Prior to negotiation the override language should be the default
|
||||
// language.
|
||||
$this->language = $default_language->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadOverrides($names) {
|
||||
if ($this->language) {
|
||||
$storage = $this->getStorage($this->language->getId());
|
||||
return $storage->readMultiple($names);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOverride($langcode, $name) {
|
||||
$storage = $this->getStorage($langcode);
|
||||
$data = $storage->read($name);
|
||||
|
||||
$override = new LanguageConfigOverride(
|
||||
$name,
|
||||
$storage,
|
||||
$this->typedConfigManager,
|
||||
$this->eventDispatcher
|
||||
);
|
||||
|
||||
if (!empty($data)) {
|
||||
$override->initWithData($data);
|
||||
}
|
||||
return $override;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStorage($langcode) {
|
||||
if (!isset($this->storages[$langcode])) {
|
||||
$this->storages[$langcode] = $this->baseStorage->createCollection($this->createConfigCollectionName($langcode));
|
||||
}
|
||||
return $this->storages[$langcode];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheSuffix() {
|
||||
return $this->language ? $this->language->getId() : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLanguage() {
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setLanguage(LanguageInterface $language = NULL) {
|
||||
$this->language = $language;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setLanguageFromDefault(LanguageDefault $language_default = NULL) {
|
||||
$this->language = $language_default ? $language_default->get() : NULL;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function installLanguageOverrides($langcode) {
|
||||
/** @var \Drupal\Core\Config\ConfigInstallerInterface $config_installer */
|
||||
$config_installer = \Drupal::service('config.installer');
|
||||
$config_installer->installCollectionDefaultConfig($this->createConfigCollectionName($langcode));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) {
|
||||
$langcode = $this->getLangcodeFromCollectionName($collection);
|
||||
return $this->getOverride($langcode, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addCollections(ConfigCollectionInfo $collection_info) {
|
||||
foreach (\Drupal::languageManager()->getLanguages() as $language) {
|
||||
$collection_info->addCollection($this->createConfigCollectionName($language->getId()), $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onConfigSave(ConfigCrudEvent $event) {
|
||||
$config = $event->getConfig();
|
||||
$name = $config->getName();
|
||||
foreach (\Drupal::languageManager()->getLanguages() as $language) {
|
||||
$config_translation = $this->getOverride($language->getId(), $name);
|
||||
if (!$config_translation->isNew()) {
|
||||
$this->filterOverride($config, $config_translation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onConfigRename(ConfigRenameEvent $event) {
|
||||
$config = $event->getConfig();
|
||||
$name = $config->getName();
|
||||
$old_name = $event->getOldName();
|
||||
foreach (\Drupal::languageManager()->getLanguages() as $language) {
|
||||
$config_translation = $this->getOverride($language->getId(), $old_name);
|
||||
if (!$config_translation->isNew()) {
|
||||
$saved_config = $config_translation->get();
|
||||
$storage = $this->getStorage($language->getId());
|
||||
$storage->write($name, $saved_config);
|
||||
$config_translation->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onConfigDelete(ConfigCrudEvent $event) {
|
||||
$config = $event->getConfig();
|
||||
$name = $config->getName();
|
||||
foreach (\Drupal::languageManager()->getLanguages() as $language) {
|
||||
$config_translation = $this->getOverride($language->getId(), $name);
|
||||
if (!$config_translation->isNew()) {
|
||||
$config_translation->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata($name) {
|
||||
$metadata = new CacheableMetadata();
|
||||
if ($this->language) {
|
||||
$metadata->setCacheContexts(['languages:language_interface']);
|
||||
}
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Config;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryOverrideInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Language\LanguageDefault;
|
||||
|
||||
/**
|
||||
* Defines the interface for a configuration factory language override object.
|
||||
*/
|
||||
interface LanguageConfigFactoryOverrideInterface extends ConfigFactoryOverrideInterface {
|
||||
|
||||
/**
|
||||
* Gets the language object used to override configuration data.
|
||||
*
|
||||
* @return \Drupal\Core\Language\LanguageInterface
|
||||
* The language object used to override configuration data.
|
||||
*/
|
||||
public function getLanguage();
|
||||
|
||||
/**
|
||||
* Sets the language to be used in configuration overrides.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageInterface $language
|
||||
* The language object used to override configuration data.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLanguage(LanguageInterface $language = NULL);
|
||||
|
||||
/**
|
||||
* Sets the language to be used in configuration overrides from the default.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageDefault $language_default
|
||||
* The default language.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @deprecated in drupal:8.3.0 and is removed from drupal:9.0.0. This
|
||||
* method has been replaced by injecting the default language into the
|
||||
* constructor.
|
||||
*/
|
||||
public function setLanguageFromDefault(LanguageDefault $language_default = NULL);
|
||||
|
||||
/**
|
||||
* Get language override for given language and configuration name.
|
||||
*
|
||||
* @param string $langcode
|
||||
* Language code.
|
||||
* @param string $name
|
||||
* Configuration name.
|
||||
*
|
||||
* @return \Drupal\Core\Config\Config
|
||||
* Configuration override object.
|
||||
*/
|
||||
public function getOverride($langcode, $name);
|
||||
|
||||
/**
|
||||
* Returns the storage instance for a particular langcode.
|
||||
*
|
||||
* @param string $langcode
|
||||
* Language code.
|
||||
*
|
||||
* @return \Drupal\Core\Config\StorageInterface
|
||||
* The storage instance for a particular langcode.
|
||||
*/
|
||||
public function getStorage($langcode);
|
||||
|
||||
/**
|
||||
* Installs available language configuration overrides for a given langcode.
|
||||
*
|
||||
* @param string $langcode
|
||||
* Language code.
|
||||
*/
|
||||
public function installLanguageOverrides($langcode);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Config;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Config\StorableConfigBase;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\Core\Config\TypedConfigManagerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* Defines language configuration overrides.
|
||||
*/
|
||||
class LanguageConfigOverride extends StorableConfigBase {
|
||||
|
||||
use LanguageConfigCollectionNameTrait;
|
||||
|
||||
/**
|
||||
* The event dispatcher.
|
||||
*
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* Constructs a language override object.
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the configuration object being overridden.
|
||||
* @param \Drupal\Core\Config\StorageInterface $storage
|
||||
* A storage controller object to use for reading and writing the
|
||||
* configuration override.
|
||||
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
|
||||
* The typed configuration manager service.
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
|
||||
* The event dispatcher.
|
||||
*/
|
||||
public function __construct($name, StorageInterface $storage, TypedConfigManagerInterface $typed_config, EventDispatcherInterface $event_dispatcher) {
|
||||
$this->name = $name;
|
||||
$this->storage = $storage;
|
||||
$this->typedConfigManager = $typed_config;
|
||||
$this->eventDispatcher = $event_dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save($has_trusted_data = FALSE) {
|
||||
if (!$has_trusted_data) {
|
||||
// @todo Use configuration schema to validate.
|
||||
// https://www.drupal.org/node/2270399
|
||||
// Perform basic data validation.
|
||||
foreach ($this->data as $key => $value) {
|
||||
$this->validateValue($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$this->storage->write($this->name, $this->data);
|
||||
// Invalidate the cache tags not only when updating, but also when creating,
|
||||
// because a language config override object uses the same cache tag as the
|
||||
// default configuration object. Hence creating a language override is like
|
||||
// an update of configuration, but only for a specific language.
|
||||
Cache::invalidateTags($this->getCacheTags());
|
||||
$this->isNew = FALSE;
|
||||
$this->eventDispatcher->dispatch(LanguageConfigOverrideEvents::SAVE_OVERRIDE, new LanguageConfigOverrideCrudEvent($this));
|
||||
$this->originalData = $this->data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete() {
|
||||
$this->data = [];
|
||||
$this->storage->delete($this->name);
|
||||
Cache::invalidateTags($this->getCacheTags());
|
||||
$this->isNew = TRUE;
|
||||
$this->eventDispatcher->dispatch(LanguageConfigOverrideEvents::DELETE_OVERRIDE, new LanguageConfigOverrideCrudEvent($this));
|
||||
$this->originalData = $this->data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language code of this language override.
|
||||
*
|
||||
* @return string
|
||||
* The language code.
|
||||
*/
|
||||
public function getLangcode() {
|
||||
return $this->getLangcodeFromCollectionName($this->getStorage()->getCollectionName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Config;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Provides a language override event for event listeners.
|
||||
*
|
||||
* @see \Drupal\Core\Config\ConfigCrudEvent
|
||||
*/
|
||||
class LanguageConfigOverrideCrudEvent extends Event {
|
||||
|
||||
/**
|
||||
* Configuration object.
|
||||
*
|
||||
* @var \Drupal\language\Config\LanguageConfigOverride
|
||||
*/
|
||||
protected $override;
|
||||
|
||||
/**
|
||||
* Constructs a configuration event object.
|
||||
*
|
||||
* @param \Drupal\language\Config\LanguageConfigOverride $override
|
||||
* Configuration object.
|
||||
*/
|
||||
public function __construct(LanguageConfigOverride $override) {
|
||||
$this->override = $override;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets configuration object.
|
||||
*
|
||||
* @return \Drupal\language\Config\LanguageConfigOverride
|
||||
* The configuration object that caused the event to fire.
|
||||
*/
|
||||
public function getLanguageConfigOverride() {
|
||||
return $this->override;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Config;
|
||||
|
||||
/**
|
||||
* Defines events for language configuration overrides.
|
||||
*
|
||||
* @see \Drupal\Core\Config\ConfigCrudEvent
|
||||
*/
|
||||
final class LanguageConfigOverrideEvents {
|
||||
|
||||
/**
|
||||
* The name of the event fired when saving the configuration override.
|
||||
*
|
||||
* This event allows you to perform custom actions whenever a language config
|
||||
* override is saved. The event listener method receives a
|
||||
* \Drupal\language\Config\LanguageConfigOverrideCrudEvent instance.
|
||||
*
|
||||
* @Event
|
||||
*
|
||||
* @see \Drupal\language\Config\LanguageConfigOverrideCrudEvent
|
||||
* @see \Drupal\language\Config\LanguageConfigOverride::save()
|
||||
* @see \Drupal\locale\LocaleConfigSubscriber
|
||||
*/
|
||||
const SAVE_OVERRIDE = 'language.save_override';
|
||||
|
||||
/**
|
||||
* The name of the event fired when deleting the configuration override.
|
||||
*
|
||||
* This event allows you to perform custom actions whenever a language config
|
||||
* override is deleted. The event listener method receives a
|
||||
* \Drupal\language\Config\LanguageConfigOverrideCrudEvent instance.
|
||||
*
|
||||
* @Event
|
||||
*
|
||||
* @see \Drupal\language\Config\LanguageConfigOverrideCrudEvent
|
||||
* @see \Drupal\language\Config\LanguageConfigOverride::delete()
|
||||
* @see \Drupal\locale\LocaleConfigSubscriber
|
||||
*/
|
||||
const DELETE_OVERRIDE = 'language.delete_override';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a language entity.
|
||||
*/
|
||||
interface ConfigurableLanguageInterface extends ConfigEntityInterface, LanguageInterface {
|
||||
|
||||
/**
|
||||
* Sets the name of the language.
|
||||
*
|
||||
* @param string $name
|
||||
* The human-readable English name of the language.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($name);
|
||||
|
||||
/**
|
||||
* Sets the weight of the language.
|
||||
*
|
||||
* @param int $weight
|
||||
* The weight, used to order languages with larger positive weights sinking
|
||||
* items toward the bottom of lists.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setWeight($weight);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,488 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Language\LanguageDefault;
|
||||
use Drupal\Core\Language\LanguageManager;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\language\Config\LanguageConfigFactoryOverrideInterface;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* Overrides default LanguageManager to provide configured languages.
|
||||
*/
|
||||
class ConfigurableLanguageManager extends LanguageManager implements ConfigurableLanguageManagerInterface {
|
||||
|
||||
/**
|
||||
* The configuration storage service.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The module handler service.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The language configuration override service.
|
||||
*
|
||||
* @var \Drupal\language\Config\LanguageConfigFactoryOverrideInterface
|
||||
*/
|
||||
protected $configFactoryOverride;
|
||||
|
||||
/**
|
||||
* The request object.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\RequestStack
|
||||
*/
|
||||
protected $requestStack;
|
||||
|
||||
/**
|
||||
* The language negotiator.
|
||||
*
|
||||
* @var \Drupal\language\LanguageNegotiatorInterface
|
||||
*/
|
||||
protected $negotiator;
|
||||
|
||||
/**
|
||||
* Local cache for language type configuration data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $languageTypes;
|
||||
|
||||
/**
|
||||
* Local cache for language type information.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $languageTypesInfo;
|
||||
|
||||
/**
|
||||
* An array of language objects keyed by language type.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageInterface[]
|
||||
*/
|
||||
protected $negotiatedLanguages;
|
||||
|
||||
/**
|
||||
* An array of language negotiation method IDs keyed by language type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $negotiatedMethods;
|
||||
|
||||
/**
|
||||
* Whether or not the language manager has been initialized.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $initialized = FALSE;
|
||||
|
||||
/**
|
||||
* Whether language types are in the process of language initialization.
|
||||
*
|
||||
* @var bool[]
|
||||
*/
|
||||
protected $initializing = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function rebuildServices() {
|
||||
\Drupal::service('kernel')->invalidateContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ConfigurableLanguageManager object.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageDefault $default_language
|
||||
* The default language service.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The configuration factory service.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler service.
|
||||
* @param \Drupal\language\Config\LanguageConfigFactoryOverrideInterface $config_override
|
||||
* The language configuration override service.
|
||||
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
||||
* The request stack object.
|
||||
*/
|
||||
public function __construct(LanguageDefault $default_language, ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, LanguageConfigFactoryOverrideInterface $config_override, RequestStack $request_stack) {
|
||||
$this->defaultLanguage = $default_language;
|
||||
$this->configFactory = $config_factory;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->configFactoryOverride = $config_override;
|
||||
$this->requestStack = $request_stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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(LanguageInterface::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() ?: ['configurable' => [], 'all' => parent::getLanguageTypes()];
|
||||
}
|
||||
return $this->languageTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinedLanguageTypesInfo() {
|
||||
if (!isset($this->languageTypesInfo)) {
|
||||
$defaults = parent::getDefinedLanguageTypesInfo();
|
||||
|
||||
$info = $this->moduleHandler->invokeAll('language_types_info');
|
||||
$language_info = $info + $defaults;
|
||||
|
||||
// Let other modules alter the list of language types.
|
||||
$this->moduleHandler->alter('language_types_info', $language_info);
|
||||
$this->languageTypesInfo = $language_info;
|
||||
}
|
||||
return $this->languageTypesInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveLanguageTypesConfiguration(array $values) {
|
||||
$config = $this->configFactory->getEditable('language.types');
|
||||
if (isset($values['configurable'])) {
|
||||
$config->set('configurable', $values['configurable']);
|
||||
}
|
||||
if (isset($values['all'])) {
|
||||
$config->set('all', $values['all']);
|
||||
}
|
||||
$config->save(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCurrentLanguage($type = LanguageInterface::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 (!isset($this->initializing[$type])) {
|
||||
$this->initializing[$type] = TRUE;
|
||||
$negotiation = $this->negotiator->initializeType($type);
|
||||
$this->negotiatedLanguages[$type] = reset($negotiation);
|
||||
$this->negotiatedMethods[$type] = key($negotiation);
|
||||
unset($this->initializing[$type]);
|
||||
}
|
||||
// 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 == LanguageInterface::TYPE_INTERFACE) {
|
||||
return new Language(['id' => LanguageInterface::LANGCODE_SYSTEM]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->negotiatedLanguages[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset($type = NULL) {
|
||||
if (!isset($type)) {
|
||||
$this->initialized = FALSE;
|
||||
$this->negotiatedLanguages = [];
|
||||
$this->negotiatedMethods = [];
|
||||
$this->languageTypes = NULL;
|
||||
$this->languageTypesInfo = NULL;
|
||||
$this->languages = [];
|
||||
if ($this->negotiator) {
|
||||
$this->negotiator->reset();
|
||||
}
|
||||
}
|
||||
elseif (isset($this->negotiatedLanguages[$type])) {
|
||||
unset($this->negotiatedLanguages[$type]);
|
||||
unset($this->negotiatedMethods[$type]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNegotiator() {
|
||||
return $this->negotiator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setNegotiator(LanguageNegotiatorInterface $negotiator) {
|
||||
$this->negotiator = $negotiator;
|
||||
$this->initialized = FALSE;
|
||||
$this->negotiatedLanguages = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLanguages($flags = LanguageInterface::STATE_CONFIGURABLE) {
|
||||
// If a config override is set, cache using that language's ID.
|
||||
if ($override_language = $this->getConfigOverrideLanguage()) {
|
||||
$static_cache_id = $override_language->getId();
|
||||
}
|
||||
else {
|
||||
$static_cache_id = $this->getCurrentLanguage()->getId();
|
||||
}
|
||||
|
||||
if (!isset($this->languages[$static_cache_id][$flags])) {
|
||||
// Initialize the language list with the default language and default
|
||||
// locked languages. These cannot be removed. This serves as a fallback
|
||||
// list if this method is invoked while the language module is installed
|
||||
// and the configuration entities for languages are not yet fully
|
||||
// imported.
|
||||
$default = $this->getDefaultLanguage();
|
||||
$languages = [$default->getId() => $default];
|
||||
$languages += $this->getDefaultLockedLanguages($default->getWeight());
|
||||
|
||||
// Load configurable languages on top of the defaults. Ideally this could
|
||||
// use the entity API to load and instantiate ConfigurableLanguage
|
||||
// objects. However the entity API depends on the language system, so that
|
||||
// would result in infinite loops. We use the configuration system
|
||||
// directly and instantiate runtime Language objects. When language
|
||||
// entities are imported those cover the default and locked languages, so
|
||||
// site-specific configuration will prevail over the fallback values.
|
||||
// Having them in the array already ensures if this is invoked in the
|
||||
// middle of importing language configuration entities, the defaults are
|
||||
// always present.
|
||||
$config_ids = $this->configFactory->listAll('language.entity.');
|
||||
foreach ($this->configFactory->loadMultiple($config_ids) as $config) {
|
||||
$data = $config->get();
|
||||
$data['name'] = $data['label'];
|
||||
$languages[$data['id']] = new Language($data);
|
||||
}
|
||||
Language::sort($languages);
|
||||
|
||||
// Filter the full list of languages based on the value of $flags.
|
||||
$this->languages[$static_cache_id][$flags] = $this->filterLanguages($languages, $flags);
|
||||
}
|
||||
|
||||
return $this->languages[$static_cache_id][$flags];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNativeLanguages() {
|
||||
$languages = $this->getLanguages(LanguageInterface::STATE_CONFIGURABLE);
|
||||
$natives = [];
|
||||
|
||||
$original_language = $this->getConfigOverrideLanguage();
|
||||
|
||||
foreach ($languages as $langcode => $language) {
|
||||
$this->setConfigOverrideLanguage($language);
|
||||
$natives[$langcode] = ConfigurableLanguage::load($langcode);
|
||||
}
|
||||
$this->setConfigOverrideLanguage($original_language);
|
||||
Language::sort($natives);
|
||||
return $natives;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateLockedLanguageWeights() {
|
||||
// Get the weight of the last configurable language.
|
||||
$configurable_languages = $this->getLanguages(LanguageInterface::STATE_CONFIGURABLE);
|
||||
$max_weight = end($configurable_languages)->getWeight();
|
||||
|
||||
$locked_languages = $this->getLanguages(LanguageInterface::STATE_LOCKED);
|
||||
// Update locked language weights to maintain the existing order, if
|
||||
// necessary.
|
||||
if (reset($locked_languages)->getWeight() <= $max_weight) {
|
||||
foreach ($locked_languages as $language) {
|
||||
// Update system languages weight.
|
||||
$max_weight++;
|
||||
ConfigurableLanguage::load($language->getId())
|
||||
->setWeight($max_weight)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFallbackCandidates(array $context = []) {
|
||||
if ($this->isMultilingual()) {
|
||||
$candidates = [];
|
||||
if (empty($context['operation']) || $context['operation'] != 'locale_lookup') {
|
||||
// If the fallback context is not locale_lookup, initialize the
|
||||
// candidates with languages ordered by weight and add
|
||||
// LanguageInterface::LANGCODE_NOT_SPECIFIED at the end. Interface
|
||||
// translation fallback should only be based on explicit configuration
|
||||
// gathered via the alter hooks below.
|
||||
$candidates = array_keys($this->getLanguages());
|
||||
$candidates[] = LanguageInterface::LANGCODE_NOT_SPECIFIED;
|
||||
$candidates = array_combine($candidates, $candidates);
|
||||
|
||||
// The first candidate should always be the desired language if
|
||||
// specified.
|
||||
if (!empty($context['langcode'])) {
|
||||
$candidates = [$context['langcode'] => $context['langcode']] + $candidates;
|
||||
}
|
||||
}
|
||||
|
||||
// Let other modules hook in and add/change candidates.
|
||||
$type = 'language_fallback_candidates';
|
||||
$types = [];
|
||||
if (!empty($context['operation'])) {
|
||||
$types[] = $type . '_' . $context['operation'];
|
||||
}
|
||||
$types[] = $type;
|
||||
$this->moduleHandler->alter($types, $candidates, $context);
|
||||
}
|
||||
else {
|
||||
$candidates = parent::getFallbackCandidates($context);
|
||||
}
|
||||
|
||||
return $candidates;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLanguageSwitchLinks($type, Url $url) {
|
||||
$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->requestStack->getCurrentRequest(), $type, $url);
|
||||
|
||||
if (!empty($result)) {
|
||||
// Allow modules to provide translations for specific links.
|
||||
$this->moduleHandler->alter('language_switch_links', $result, $type, $url);
|
||||
$links = (object) ['links' => $result, 'method_id' => $method_id];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configuration override language.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageInterface $language
|
||||
* The language to override configuration with.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setConfigOverrideLanguage(LanguageInterface $language = NULL) {
|
||||
$this->configFactoryOverride->setLanguage($language);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfigOverrideLanguage() {
|
||||
return $this->configFactoryOverride->getLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLanguageConfigOverride($langcode, $name) {
|
||||
return $this->configFactoryOverride->getOverride($langcode, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLanguageConfigOverrideStorage($langcode) {
|
||||
return $this->configFactoryOverride->getStorage($langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStandardLanguageListWithoutConfigured() {
|
||||
$languages = $this->getLanguages();
|
||||
$predefined = $this->getStandardLanguageList();
|
||||
foreach ($predefined as $key => $value) {
|
||||
if (isset($languages[$key])) {
|
||||
unset($predefined[$key]);
|
||||
continue;
|
||||
}
|
||||
$predefined[$key] = new TranslatableMarkup($value[0]);
|
||||
}
|
||||
natcasesort($predefined);
|
||||
return $predefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNegotiatedLanguageMethod($type = LanguageInterface::TYPE_INTERFACE) {
|
||||
if (isset($this->negotiatedLanguages[$type]) && isset($this->negotiatedMethods[$type])) {
|
||||
return $this->negotiatedMethods[$type];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
|
||||
/**
|
||||
* Common interface for language negotiation services.
|
||||
*/
|
||||
interface ConfigurableLanguageManagerInterface extends LanguageManagerInterface {
|
||||
|
||||
/**
|
||||
* Rebuild the container to register services needed on multilingual sites.
|
||||
*/
|
||||
public static function rebuildServices();
|
||||
|
||||
/**
|
||||
* Returns the language negotiator.
|
||||
*
|
||||
* @return \Drupal\language\LanguageNegotiatorInterface
|
||||
* The language negotiator.
|
||||
*/
|
||||
public function getNegotiator();
|
||||
|
||||
/**
|
||||
* Injects the language negotiator.
|
||||
*
|
||||
* @param \Drupal\language\LanguageNegotiatorInterface $negotiator
|
||||
* The language negotiator.
|
||||
*/
|
||||
public function setNegotiator(LanguageNegotiatorInterface $negotiator);
|
||||
|
||||
/**
|
||||
* Returns all the defined language types including fixed ones.
|
||||
*
|
||||
* 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 array
|
||||
* An array of language type machine names.
|
||||
*/
|
||||
public function getDefinedLanguageTypes();
|
||||
|
||||
/**
|
||||
* Stores language types configuration.
|
||||
*
|
||||
* @param array $config
|
||||
* An indexed array with the following keys_
|
||||
* - configurable: an array of configurable language type names.
|
||||
* - all: an array of all the defined language type names.
|
||||
*/
|
||||
public function saveLanguageTypesConfiguration(array $config);
|
||||
|
||||
/**
|
||||
* Updates locked system language weights.
|
||||
*/
|
||||
public function updateLockedLanguageWeights();
|
||||
|
||||
/**
|
||||
* Gets a language config override object.
|
||||
*
|
||||
* @param string $langcode
|
||||
* The language code for the override.
|
||||
* @param string $name
|
||||
* The language configuration object name.
|
||||
*
|
||||
* @return \Drupal\language\Config\LanguageConfigOverride
|
||||
* The language config override object.
|
||||
*/
|
||||
public function getLanguageConfigOverride($langcode, $name);
|
||||
|
||||
/**
|
||||
* Gets a language configuration override storage object.
|
||||
*
|
||||
* @param string $langcode
|
||||
* The language code for the override.
|
||||
*
|
||||
* @return \Drupal\Core\Config\StorageInterface
|
||||
* A storage object to use for reading and writing the
|
||||
* configuration override.
|
||||
*/
|
||||
public function getLanguageConfigOverrideStorage($langcode);
|
||||
|
||||
/**
|
||||
* Returns the standard language list excluding already configured languages.
|
||||
*
|
||||
* @return array
|
||||
* A list of standard language names keyed by langcode.
|
||||
*/
|
||||
public function getStandardLanguageListWithoutConfigured();
|
||||
|
||||
/**
|
||||
* Gets the negotiated language method ID.
|
||||
*
|
||||
* @param string $type
|
||||
* (optional) The language type; e.g., the interface or the content
|
||||
* language.
|
||||
*
|
||||
* @return string
|
||||
* The negotiated language method ID.
|
||||
*/
|
||||
public function getNegotiatedLanguageMethod($type = LanguageInterface::TYPE_INTERFACE);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
/**
|
||||
* Exception thrown by ContentLanguageSettings when target bundle is not set.
|
||||
*/
|
||||
class ContentLanguageSettingsException extends \RuntimeException {}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining language settings for content entities.
|
||||
*/
|
||||
interface ContentLanguageSettingsInterface extends ConfigEntityInterface {
|
||||
|
||||
/**
|
||||
* Gets the entity type ID this config applies to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTargetEntityTypeId();
|
||||
|
||||
/**
|
||||
* Gets the bundle this config applies to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTargetBundle();
|
||||
|
||||
/**
|
||||
* Sets the bundle this config applies to.
|
||||
*
|
||||
* @param string $target_bundle
|
||||
* The bundle.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTargetBundle($target_bundle);
|
||||
|
||||
/**
|
||||
* Sets the default language code.
|
||||
*
|
||||
* @param string $default_langcode
|
||||
* The default language code.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDefaultLangcode($default_langcode);
|
||||
|
||||
/**
|
||||
* Gets the default language code.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultLangcode();
|
||||
|
||||
/**
|
||||
* Sets if the language must be alterable or not.
|
||||
*
|
||||
* @param bool $language_alterable
|
||||
* Flag indicating if the language must be alterable.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLanguageAlterable($language_alterable);
|
||||
|
||||
/**
|
||||
* Checks if the language is alterable or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLanguageAlterable();
|
||||
|
||||
/**
|
||||
* Checks if this config object contains the default values in every property.
|
||||
*
|
||||
* @return bool
|
||||
* True if all the properties contain the default values. False otherwise.
|
||||
*/
|
||||
public function isDefaultConfiguration();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\LanguageItem;
|
||||
use Drupal\Core\Language\Language;
|
||||
|
||||
/**
|
||||
* Alternative plugin implementation of the 'language' field type.
|
||||
*
|
||||
* Replaces the Core 'language' entity field type implementation, changes the
|
||||
* default values used.
|
||||
*
|
||||
* Required settings are:
|
||||
* - target_type: The entity type to reference.
|
||||
*
|
||||
* @see language_field_info_alter().
|
||||
*/
|
||||
class DefaultLanguageItem extends LanguageItem {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applyDefaultValue($notify = TRUE) {
|
||||
// Default to LANGCODE_NOT_SPECIFIED.
|
||||
$langcode = Language::LANGCODE_NOT_SPECIFIED;
|
||||
if ($entity = $this->getEntity()) {
|
||||
$langcode = $this->getDefaultLangcode($entity);
|
||||
}
|
||||
// Always notify otherwise default langcode will not be set correctly.
|
||||
$this->setValue(['value' => $langcode], TRUE);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides default language code of given entity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity whose language code to be loaded.
|
||||
*
|
||||
* @return string
|
||||
* A string language code.
|
||||
*/
|
||||
public function getDefaultLangcode(EntityInterface $entity) {
|
||||
return language_get_default_langcode($entity->getEntityTypeId(), $entity->bundle());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Element;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
|
||||
/**
|
||||
* Defines an element for language configuration for a single field.
|
||||
*
|
||||
* @FormElement("language_configuration")
|
||||
*/
|
||||
class LanguageConfiguration extends FormElement {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return [
|
||||
'#input' => TRUE,
|
||||
'#tree' => TRUE,
|
||||
'#process' => [
|
||||
[$class, 'processLanguageConfiguration'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Process handler for the language_configuration form element.
|
||||
*/
|
||||
public static function processLanguageConfiguration(&$element, FormStateInterface $form_state, &$form) {
|
||||
$options = isset($element['#options']) ? $element['#options'] : [];
|
||||
// Avoid validation failure since we are moving the '#options' key in the
|
||||
// nested 'language' select element.
|
||||
unset($element['#options']);
|
||||
/** @var \Drupal\language\Entity\ContentLanguageSettings $default_config */
|
||||
$default_config = $element['#default_value'];
|
||||
$element['langcode'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Default language'),
|
||||
'#options' => $options + static::getDefaultOptions(),
|
||||
'#description' => t('Explanation of the language options is found on the <a href=":languages_list_page">languages list page</a>.', [':languages_list_page' => Url::fromRoute('entity.configurable_language.collection')->toString()]),
|
||||
'#default_value' => ($default_config != NULL) ? $default_config->getDefaultLangcode() : LanguageInterface::LANGCODE_SITE_DEFAULT,
|
||||
];
|
||||
|
||||
$element['language_alterable'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Show language selector on create and edit pages'),
|
||||
'#default_value' => ($default_config != NULL) ? $default_config->isLanguageAlterable() : FALSE,
|
||||
];
|
||||
|
||||
// Add the entity type and bundle information to the form if they are set.
|
||||
// They will be used, in the submit handler, to generate the names of the
|
||||
// configuration entities that will store the settings and are a way to uniquely
|
||||
// identify the entity.
|
||||
$language = $form_state->get('language') ?: [];
|
||||
$language += [
|
||||
$element['#name'] => [
|
||||
'entity_type' => $element['#entity_information']['entity_type'],
|
||||
'bundle' => $element['#entity_information']['bundle'],
|
||||
],
|
||||
];
|
||||
$form_state->set('language', $language);
|
||||
|
||||
// Do not add the submit callback for the language content settings page,
|
||||
// which is handled separately.
|
||||
if ($form['#form_id'] != 'language_content_settings_form') {
|
||||
// Determine where to attach the language_configuration element submit
|
||||
// handler.
|
||||
// @todo Form API: Allow form widgets/sections to declare #submit
|
||||
// handlers.
|
||||
$submit_name = isset($form['actions']['save_continue']) ? 'save_continue' : 'submit';
|
||||
if (isset($form['actions'][$submit_name]['#submit']) && array_search('language_configuration_element_submit', $form['actions'][$submit_name]['#submit']) === FALSE) {
|
||||
$form['actions'][$submit_name]['#submit'][] = 'language_configuration_element_submit';
|
||||
}
|
||||
elseif (array_search('language_configuration_element_submit', $form['#submit']) === FALSE) {
|
||||
$form['#submit'][] = 'language_configuration_element_submit';
|
||||
}
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default options for the language configuration form element.
|
||||
*
|
||||
* @return array
|
||||
* An array containing the default options.
|
||||
*/
|
||||
protected static function getDefaultOptions() {
|
||||
$language_options = [
|
||||
LanguageInterface::LANGCODE_SITE_DEFAULT => t("Site's default language (@language)", ['@language' => static::languageManager()->getDefaultLanguage()->getName()]),
|
||||
'current_interface' => t('Interface text language selected for page'),
|
||||
'authors_default' => t("Author's preferred language"),
|
||||
];
|
||||
|
||||
$languages = static::languageManager()->getLanguages(LanguageInterface::STATE_ALL);
|
||||
foreach ($languages as $langcode => $language) {
|
||||
$language_options[$langcode] = $language->isLocked() ? t('- @name -', ['@name' => $language->getName()]) : $language->getName();
|
||||
}
|
||||
|
||||
return $language_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the language manager.
|
||||
*
|
||||
* @return \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected static function languageManager() {
|
||||
return \Drupal::languageManager();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Language\LanguageManager;
|
||||
use Drupal\language\ConfigurableLanguageManager;
|
||||
use Drupal\language\ConfigurableLanguageManagerInterface;
|
||||
use Drupal\language\Exception\DeleteDefaultLanguageException;
|
||||
use Drupal\language\ConfigurableLanguageInterface;
|
||||
|
||||
/**
|
||||
* Defines the ConfigurableLanguage entity.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "configurable_language",
|
||||
* label = @Translation("Language"),
|
||||
* label_collection = @Translation("Languages"),
|
||||
* label_singular = @Translation("language"),
|
||||
* label_plural = @Translation("languages"),
|
||||
* label_count = @PluralTranslation(
|
||||
* singular = "@count language",
|
||||
* plural = "@count languages",
|
||||
* ),
|
||||
* handlers = {
|
||||
* "list_builder" = "Drupal\language\LanguageListBuilder",
|
||||
* "access" = "Drupal\language\LanguageAccessControlHandler",
|
||||
* "form" = {
|
||||
* "add" = "Drupal\language\Form\LanguageAddForm",
|
||||
* "edit" = "Drupal\language\Form\LanguageEditForm",
|
||||
* "delete" = "Drupal\language\Form\LanguageDeleteForm"
|
||||
* }
|
||||
* },
|
||||
* admin_permission = "administer languages",
|
||||
* config_prefix = "entity",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "label",
|
||||
* "weight" = "weight"
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "label",
|
||||
* "direction",
|
||||
* "weight",
|
||||
* "locked"
|
||||
* },
|
||||
* links = {
|
||||
* "delete-form" = "/admin/config/regional/language/delete/{configurable_language}",
|
||||
* "edit-form" = "/admin/config/regional/language/edit/{configurable_language}",
|
||||
* "collection" = "/admin/config/regional/language",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class ConfigurableLanguage extends ConfigEntityBase implements ConfigurableLanguageInterface {
|
||||
|
||||
/**
|
||||
* The language ID (machine name).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The human-readable label for the language.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* The direction of language, either DIRECTION_LTR or DIRECTION_RTL.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $direction = self::DIRECTION_LTR;
|
||||
|
||||
/**
|
||||
* The weight of the language, used in lists of languages.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $weight = 0;
|
||||
|
||||
/**
|
||||
* Locked languages cannot be edited.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $locked = FALSE;
|
||||
|
||||
/**
|
||||
* Used during saving to detect when the site becomes multilingual.
|
||||
*
|
||||
* This property is not saved to the language entity, but is needed for
|
||||
* detecting when to rebuild the services.
|
||||
*
|
||||
* @see \Drupal\language\Entity\ConfigurableLanguage::preSave()
|
||||
* @see \Drupal\language\Entity\ConfigurableLanguage::postSave()
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $preSaveMultilingual;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isDefault() {
|
||||
return static::getDefaultLangcode() == $this->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isLocked() {
|
||||
return (bool) $this->locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageInterface $storage) {
|
||||
parent::preSave($storage);
|
||||
// Store whether or not the site is already multilingual so that we can
|
||||
// rebuild services if necessary during
|
||||
// \Drupal\language\Entity\ConfigurableLanguage::postSave().
|
||||
$this->preSaveMultilingual = \Drupal::languageManager()->isMultilingual();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
|
||||
parent::postSave($storage, $update);
|
||||
|
||||
$language_manager = \Drupal::languageManager();
|
||||
$language_manager->reset();
|
||||
if (!$this->isLocked() && $language_manager instanceof ConfigurableLanguageManagerInterface && !$this->isSyncing()) {
|
||||
$language_manager->updateLockedLanguageWeights();
|
||||
}
|
||||
|
||||
// Update URL Prefixes for all languages after the
|
||||
// LanguageManagerInterface::getLanguages() cache is flushed.
|
||||
language_negotiation_url_prefixes_update();
|
||||
|
||||
// If after adding this language the site will become multilingual, we need
|
||||
// to rebuild language services.
|
||||
if (!$this->preSaveMultilingual && !$update && $language_manager instanceof ConfigurableLanguageManagerInterface) {
|
||||
$language_manager::rebuildServices();
|
||||
}
|
||||
if (!$update) {
|
||||
// Install any available language configuration overrides for the language.
|
||||
\Drupal::service('language.config_factory_override')->installLanguageOverrides($this->id());
|
||||
}
|
||||
|
||||
if (!$this->isLocked() && !$update) {
|
||||
// Add language to the list of language domains.
|
||||
$config = \Drupal::configFactory()->getEditable('language.negotiation');
|
||||
$domains = $config->get('url.domains');
|
||||
$domains[$this->id()] = '';
|
||||
$config->set('url.domains', $domains)->save(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws \DeleteDefaultLanguageException
|
||||
* Exception thrown if we're trying to delete the default language entity.
|
||||
* This is not allowed as a site must have a default language.
|
||||
*/
|
||||
public static function preDelete(EntityStorageInterface $storage, array $entities) {
|
||||
$default_langcode = static::getDefaultLangcode();
|
||||
foreach ($entities as $entity) {
|
||||
if ($entity->id() == $default_langcode && !$entity->isUninstalling()) {
|
||||
throw new DeleteDefaultLanguageException('Can not delete the default language');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageInterface $storage, array $entities) {
|
||||
parent::postDelete($storage, $entities);
|
||||
$language_manager = \Drupal::languageManager();
|
||||
$language_manager->reset();
|
||||
$entity = reset($entities);
|
||||
if ($language_manager instanceof ConfigurableLanguageManagerInterface && !$entity->isUninstalling() && !$entity->isSyncing()) {
|
||||
$language_manager->updateLockedLanguageWeights();
|
||||
}
|
||||
// If after deleting this language the site will become monolingual, we need
|
||||
// to rebuild language services.
|
||||
if (!\Drupal::languageManager()->isMultilingual()) {
|
||||
ConfigurableLanguageManager::rebuildServices();
|
||||
}
|
||||
|
||||
// Remove language from language prefix and domain list.
|
||||
$config = \Drupal::configFactory()->getEditable('language.negotiation');
|
||||
$config->clear('url.prefixes.' . $entity->id());
|
||||
$config->clear('url.domains.' . $entity->id());
|
||||
$config->save(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default langcode.
|
||||
*
|
||||
* @return string
|
||||
* The current default langcode.
|
||||
*/
|
||||
protected static function getDefaultLangcode() {
|
||||
$language = \Drupal::service('language.default')->get();
|
||||
return $language->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return $this->label();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setName($name) {
|
||||
$this->label = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId() {
|
||||
return $this->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDirection() {
|
||||
return $this->direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWeight() {
|
||||
return $this->weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setWeight($weight) {
|
||||
$this->weight = $weight;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a configurable language object from a langcode.
|
||||
*
|
||||
* @param string $langcode
|
||||
* The language code to use to create the object.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @see \Drupal\Core\Language\LanguageManager::getStandardLanguageList()
|
||||
*/
|
||||
public static function createFromLangcode($langcode) {
|
||||
$standard_languages = LanguageManager::getStandardLanguageList();
|
||||
if (!isset($standard_languages[$langcode])) {
|
||||
// Drupal does not know about this language, so we set its values with the
|
||||
// best guess. The user will be able to edit afterwards.
|
||||
return static::create([
|
||||
'id' => $langcode,
|
||||
'label' => $langcode,
|
||||
]);
|
||||
}
|
||||
else {
|
||||
// A known predefined language, details will be filled in properly.
|
||||
return static::create([
|
||||
'id' => $langcode,
|
||||
'label' => $standard_languages[$langcode][0],
|
||||
'direction' => isset($standard_languages[$langcode][2]) ? $standard_languages[$langcode][2] : static::DIRECTION_LTR,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\language\ContentLanguageSettingsException;
|
||||
use Drupal\language\ContentLanguageSettingsInterface;
|
||||
|
||||
/**
|
||||
* Defines the ContentLanguageSettings entity.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "language_content_settings",
|
||||
* label = @Translation("Content Language Settings"),
|
||||
* label_collection = @Translation("Content Language Settings"),
|
||||
* label_singular = @Translation("content language setting"),
|
||||
* label_plural = @Translation("content languages settings"),
|
||||
* label_count = @PluralTranslation(
|
||||
* singular = "@count content language setting",
|
||||
* plural = "@count content languages settings",
|
||||
* ),
|
||||
* admin_permission = "administer languages",
|
||||
* config_prefix = "content_settings",
|
||||
* entity_keys = {
|
||||
* "id" = "id"
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "target_entity_type_id",
|
||||
* "target_bundle",
|
||||
* "default_langcode",
|
||||
* "language_alterable",
|
||||
* },
|
||||
* list_cache_tags = { "rendered" }
|
||||
* )
|
||||
*/
|
||||
class ContentLanguageSettings extends ConfigEntityBase implements ContentLanguageSettingsInterface {
|
||||
|
||||
/**
|
||||
* The id. Combination of $target_entity_type_id.$target_bundle.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The entity type ID (machine name).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $target_entity_type_id;
|
||||
|
||||
/**
|
||||
* The bundle (machine name).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $target_bundle;
|
||||
|
||||
/**
|
||||
* The default language code.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $default_langcode = LanguageInterface::LANGCODE_SITE_DEFAULT;
|
||||
|
||||
/**
|
||||
* Indicates if the language is alterable or not.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $language_alterable = FALSE;
|
||||
|
||||
/**
|
||||
* Constructs a ContentLanguageSettings object.
|
||||
*
|
||||
* In most cases, Field entities are created via
|
||||
* FieldConfig::create($values), where $values is the same
|
||||
* parameter as in this constructor.
|
||||
*
|
||||
* @param array $values
|
||||
* An array of the referring entity bundle with:
|
||||
* - target_entity_type_id: The entity type.
|
||||
* - target_bundle: The bundle.
|
||||
* Other array elements will be used to set the corresponding properties on
|
||||
* the class; see the class property documentation for details.
|
||||
*/
|
||||
public function __construct(array $values, $entity_type = 'language_content_settings') {
|
||||
if (empty($values['target_entity_type_id'])) {
|
||||
throw new ContentLanguageSettingsException('Attempt to create content language settings without a target_entity_type_id.');
|
||||
}
|
||||
if (empty($values['target_bundle'])) {
|
||||
throw new ContentLanguageSettingsException('Attempt to create content language settings without a target_bundle.');
|
||||
}
|
||||
parent::__construct($values, $entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function id() {
|
||||
return $this->target_entity_type_id . '.' . $this->target_bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTargetEntityTypeId() {
|
||||
return $this->target_entity_type_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTargetBundle() {
|
||||
return $this->target_bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setTargetBundle($target_bundle) {
|
||||
$this->target_bundle = $target_bundle;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDefaultLangcode($default_langcode) {
|
||||
$this->default_langcode = $default_langcode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultLangcode() {
|
||||
return $this->default_langcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setLanguageAlterable($language_alterable) {
|
||||
$this->language_alterable = $language_alterable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isLanguageAlterable() {
|
||||
return $this->language_alterable;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageInterface $storage) {
|
||||
$this->id = $this->id();
|
||||
parent::preSave($storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isDefaultConfiguration() {
|
||||
return (!$this->language_alterable && $this->default_langcode == LanguageInterface::LANGCODE_SITE_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a content language config entity based on the entity type and bundle.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* ID of the entity type.
|
||||
* @param string $bundle
|
||||
* Bundle name.
|
||||
*
|
||||
* @return $this
|
||||
* The content language config entity if one exists. Otherwise, returns
|
||||
* default values.
|
||||
*/
|
||||
public static function loadByEntityTypeBundle($entity_type_id, $bundle) {
|
||||
if ($entity_type_id == NULL || $bundle == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
$config = \Drupal::entityTypeManager()->getStorage('language_content_settings')->load($entity_type_id . '.' . $bundle);
|
||||
if ($config == NULL) {
|
||||
$config = ContentLanguageSettings::create(['target_entity_type_id' => $entity_type_id, 'target_bundle' => $bundle]);
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
parent::calculateDependencies();
|
||||
|
||||
// Create dependency on the bundle.
|
||||
$entity_type = \Drupal::entityTypeManager()->getDefinition($this->target_entity_type_id);
|
||||
$bundle_config_dependency = $entity_type->getBundleConfigDependency($this->target_bundle);
|
||||
$this->addDependency($bundle_config_dependency['type'], $bundle_config_dependency['name']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Language\LanguageDefault;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Config\ConfigCrudEvent;
|
||||
use Drupal\Core\Config\ConfigEvents;
|
||||
use Drupal\language\ConfigurableLanguageManager;
|
||||
use Drupal\language\HttpKernel\PathProcessorLanguage;
|
||||
use Drupal\language\LanguageNegotiatorInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Deletes the container if default language has changed.
|
||||
*/
|
||||
class ConfigSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The default language.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageDefault
|
||||
*/
|
||||
protected $languageDefault;
|
||||
|
||||
/**
|
||||
* The configuration factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The language negotiator.
|
||||
*
|
||||
* @var \Drupal\language\LanguageNegotiatorInterface
|
||||
*/
|
||||
protected $languageNegotiator;
|
||||
|
||||
/**
|
||||
* The language path processor.
|
||||
*
|
||||
* @var \Drupal\language\HttpKernel\PathProcessorLanguage
|
||||
*/
|
||||
protected $pathProcessorLanguage;
|
||||
|
||||
/**
|
||||
* Constructs a new class object.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\Language\LanguageDefault $language_default
|
||||
* The default language.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The configuration factory.
|
||||
* @param \Drupal\language\LanguageNegotiatorInterface $language_negotiator
|
||||
* The language negotiator.
|
||||
*/
|
||||
public function __construct(LanguageManagerInterface $language_manager, LanguageDefault $language_default, ConfigFactoryInterface $config_factory, LanguageNegotiatorInterface $language_negotiator) {
|
||||
$this->languageManager = $language_manager;
|
||||
$this->languageDefault = $language_default;
|
||||
$this->configFactory = $config_factory;
|
||||
$this->languageNegotiator = $language_negotiator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes the container to be rebuilt on the next request.
|
||||
*
|
||||
* This event subscriber assumes that the new default langcode and old default
|
||||
* langcode are valid langcodes. If the schema definition of either
|
||||
* system.site:default_langcode or language.negotiation::url.prefixes changes
|
||||
* then this event must be changed to work with both the old and new schema
|
||||
* definition so this event is update safe.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigCrudEvent $event
|
||||
* The configuration event.
|
||||
*/
|
||||
public function onConfigSave(ConfigCrudEvent $event) {
|
||||
$saved_config = $event->getConfig();
|
||||
if ($saved_config->getName() == 'system.site' && $event->isChanged('default_langcode')) {
|
||||
$new_default_langcode = $saved_config->get('default_langcode');
|
||||
$default_language = $this->configFactory->get('language.entity.' . $new_default_langcode);
|
||||
// During an import the language might not exist yet.
|
||||
if (!$default_language->isNew()) {
|
||||
$this->languageDefault->set(new Language($default_language->get()));
|
||||
$this->languageManager->reset();
|
||||
|
||||
// Directly update language negotiation settings instead of calling
|
||||
// language_negotiation_url_prefixes_update() to ensure that the code
|
||||
// obeys the hook_update_N() restrictions.
|
||||
$negotiation_config = $this->configFactory->getEditable('language.negotiation');
|
||||
$negotiation_changed = FALSE;
|
||||
$url_prefixes = $negotiation_config->get('url.prefixes');
|
||||
$old_default_langcode = $saved_config->getOriginal('default_langcode');
|
||||
if (empty($url_prefixes[$old_default_langcode])) {
|
||||
$negotiation_config->set('url.prefixes.' . $old_default_langcode, $old_default_langcode);
|
||||
$negotiation_changed = TRUE;
|
||||
}
|
||||
if (empty($url_prefixes[$new_default_langcode])) {
|
||||
$negotiation_config->set('url.prefixes.' . $new_default_langcode, '');
|
||||
$negotiation_changed = TRUE;
|
||||
}
|
||||
if ($negotiation_changed) {
|
||||
$negotiation_config->save(TRUE);
|
||||
}
|
||||
}
|
||||
// Trigger a container rebuild on the next request by invalidating it.
|
||||
ConfigurableLanguageManager::rebuildServices();
|
||||
}
|
||||
elseif ($saved_config->getName() == 'language.types' && $event->isChanged('negotiation')) {
|
||||
// If the negotiation configuration changed the language negotiator and
|
||||
// the language path processor have to be reset so that they regenerate
|
||||
// the method instances and also sort them accordingly to the new config.
|
||||
$this->languageNegotiator->reset();
|
||||
if (isset($this->pathProcessorLanguage)) {
|
||||
$this->pathProcessorLanguage->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the language path processors on multilingual site configuration.
|
||||
*
|
||||
* @param \Drupal\language\HttpKernel\PathProcessorLanguage $path_processor_language
|
||||
* The language path processor.
|
||||
*/
|
||||
public function setPathProcessorLanguage(PathProcessorLanguage $path_processor_language) {
|
||||
$this->pathProcessorLanguage = $path_processor_language;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events[ConfigEvents::SAVE][] = ['onConfigSave', 0];
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\EventSubscriber;
|
||||
|
||||
use Drupal\Core\DrupalKernelInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\StringTranslation\Translator\TranslatorInterface;
|
||||
use Drupal\language\ConfigurableLanguageManagerInterface;
|
||||
use Drupal\language\LanguageNegotiatorInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Sets the $request property on the language manager.
|
||||
*/
|
||||
class LanguageRequestSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The language manager service.
|
||||
*
|
||||
* @var \Drupal\language\ConfigurableLanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The language negotiator.
|
||||
*
|
||||
* @var \Drupal\language\LanguageNegotiatorInterface
|
||||
*/
|
||||
protected $negotiator;
|
||||
|
||||
/**
|
||||
* The translation service.
|
||||
*
|
||||
* @var \Drupal\Core\StringTranslation\Translator\TranslatorInterface
|
||||
*/
|
||||
protected $translation;
|
||||
|
||||
/**
|
||||
* The current active user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* Constructs a LanguageRequestSubscriber object.
|
||||
*
|
||||
* @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
|
||||
* The language manager service.
|
||||
* @param \Drupal\language\LanguageNegotiatorInterface $negotiator
|
||||
* The language negotiator.
|
||||
* @param \Drupal\Core\StringTranslation\Translator\TranslatorInterface $translation
|
||||
* The translation service.
|
||||
* @param \Drupal\Core\Session\AccountInterface $current_user
|
||||
* The current active user.
|
||||
*/
|
||||
public function __construct(ConfigurableLanguageManagerInterface $language_manager, LanguageNegotiatorInterface $negotiator, TranslatorInterface $translation, AccountInterface $current_user) {
|
||||
$this->languageManager = $language_manager;
|
||||
$this->negotiator = $negotiator;
|
||||
$this->translation = $translation;
|
||||
$this->currentUser = $current_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the language manager at the beginning of the request.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
|
||||
* The Event to process.
|
||||
*/
|
||||
public function onKernelRequestLanguage(GetResponseEvent $event) {
|
||||
if ($event->isMasterRequest()) {
|
||||
$this->setLanguageOverrides();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes config overrides whenever the service container is rebuilt.
|
||||
*/
|
||||
public function onContainerInitializeSubrequestFinished() {
|
||||
$this->setLanguageOverrides();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the language for config overrides on the language manager.
|
||||
*/
|
||||
private function setLanguageOverrides() {
|
||||
$this->negotiator->setCurrentUser($this->currentUser);
|
||||
if ($this->languageManager instanceof ConfigurableLanguageManagerInterface) {
|
||||
$this->languageManager->setNegotiator($this->negotiator);
|
||||
$this->languageManager->setConfigOverrideLanguage($this->languageManager->getCurrentLanguage());
|
||||
}
|
||||
// After the language manager has initialized, set the default langcode for
|
||||
// the string translations.
|
||||
$langcode = $this->languageManager->getCurrentLanguage()->getId();
|
||||
$this->translation->setDefaultLangcode($langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the methods in this class that should be listeners.
|
||||
*
|
||||
* @return array
|
||||
* An array of event listener definitions.
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events[KernelEvents::REQUEST][] = ['onKernelRequestLanguage', 255];
|
||||
$events[DrupalKernelInterface::CONTAINER_INITIALIZE_SUBREQUEST_FINISHED][] = ['onContainerInitializeSubrequestFinished', 255];
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when deleting the default language.
|
||||
*/
|
||||
class DeleteDefaultLanguageException extends LanguageException {}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Exception;
|
||||
|
||||
/**
|
||||
* A base exception thrown in any language system operations.
|
||||
*/
|
||||
class LanguageException extends \RuntimeException {}
|
||||
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Form;
|
||||
|
||||
use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\language\Entity\ContentLanguageSettings;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Configure the content language settings for this site.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ContentLanguageSettingsForm extends FormBase {
|
||||
use DeprecatedServicePropertyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $deprecatedProperties = ['entityManager' => 'entity.manager'];
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The entity bundle info.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* If this validator can handle multiple arguments.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $multipleCapable = TRUE;
|
||||
|
||||
/**
|
||||
* Constructs an \Drupal\views\Plugin\views\argument_validator\Entity object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
|
||||
* The entity type bundle info.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
if (!$entity_type_bundle_info) {
|
||||
@trigger_error('Calling ContentLanguageSettingsForm::__construct() with the $entity_type_bundle_info argument is supported in drupal:8.7.0 and will be required before drupal:9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED);
|
||||
$entity_type_bundle_info = \Drupal::service('entity_type.bundle.info');
|
||||
}
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('entity_type.bundle.info')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'language_content_settings_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$entity_types = $this->entityTypeManager->getDefinitions();
|
||||
$labels = [];
|
||||
$default = [];
|
||||
|
||||
$bundles = $this->entityTypeBundleInfo->getAllBundleInfo();
|
||||
$language_configuration = [];
|
||||
foreach ($entity_types as $entity_type_id => $entity_type) {
|
||||
if (!$entity_type instanceof ContentEntityTypeInterface || !$entity_type->hasKey('langcode') || !isset($bundles[$entity_type_id])) {
|
||||
continue;
|
||||
}
|
||||
$labels[$entity_type_id] = $entity_type->getLabel() ?: $entity_type_id;
|
||||
$default[$entity_type_id] = FALSE;
|
||||
|
||||
// Check whether we have any custom setting.
|
||||
foreach ($bundles[$entity_type_id] as $bundle => $bundle_info) {
|
||||
$config = ContentLanguageSettings::loadByEntityTypeBundle($entity_type_id, $bundle);
|
||||
if (!$config->isDefaultConfiguration()) {
|
||||
$default[$entity_type_id] = $entity_type_id;
|
||||
}
|
||||
$language_configuration[$entity_type_id][$bundle] = $config;
|
||||
}
|
||||
}
|
||||
|
||||
asort($labels);
|
||||
|
||||
$form = [
|
||||
'#labels' => $labels,
|
||||
'#attached' => [
|
||||
'library' => [
|
||||
'language/drupal.language.admin',
|
||||
],
|
||||
],
|
||||
'#attributes' => [
|
||||
'class' => 'language-content-settings-form',
|
||||
],
|
||||
];
|
||||
|
||||
$form['entity_types'] = [
|
||||
'#title' => $this->t('Custom language settings'),
|
||||
'#type' => 'checkboxes',
|
||||
'#options' => $labels,
|
||||
'#default_value' => $default,
|
||||
];
|
||||
|
||||
$form['settings'] = ['#tree' => TRUE];
|
||||
|
||||
foreach ($labels as $entity_type_id => $label) {
|
||||
$entity_type = $entity_types[$entity_type_id];
|
||||
|
||||
$form['settings'][$entity_type_id] = [
|
||||
'#title' => $label,
|
||||
'#type' => 'container',
|
||||
'#entity_type' => $entity_type_id,
|
||||
'#theme' => 'language_content_settings_table',
|
||||
'#bundle_label' => $entity_type->getBundleLabel() ?: $label,
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
':input[name="entity_types[' . $entity_type_id . ']"]' => ['checked' => TRUE],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($bundles[$entity_type_id] as $bundle => $bundle_info) {
|
||||
$form['settings'][$entity_type_id][$bundle]['settings'] = [
|
||||
'#type' => 'item',
|
||||
'#label' => $bundle_info['label'],
|
||||
'language' => [
|
||||
'#type' => 'language_configuration',
|
||||
'#entity_information' => [
|
||||
'entity_type' => $entity_type_id,
|
||||
'bundle' => $bundle,
|
||||
],
|
||||
'#default_value' => $language_configuration[$entity_type_id][$bundle],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$form['actions']['#type'] = 'actions';
|
||||
$form['actions']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save configuration'),
|
||||
'#button_type' => 'primary',
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$entity_types = $form_state->getValue('entity_types');
|
||||
foreach ($form_state->getValue('settings') as $entity_type => $entity_settings) {
|
||||
foreach ($entity_settings as $bundle => $bundle_settings) {
|
||||
$config = ContentLanguageSettings::loadByEntityTypeBundle($entity_type, $bundle);
|
||||
if (empty($entity_types[$entity_type])) {
|
||||
$bundle_settings['settings']['language']['language_alterable'] = FALSE;
|
||||
}
|
||||
$config->setDefaultLangcode($bundle_settings['settings']['language']['langcode'])
|
||||
->setLanguageAlterable($bundle_settings['settings']['language']['language_alterable'])
|
||||
->save();
|
||||
}
|
||||
}
|
||||
$this->messenger()->addStatus($this->t('Settings successfully updated.'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageManager;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Controller for language addition forms.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class LanguageAddForm extends LanguageFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
// @todo Remove in favor of base method.
|
||||
return 'language_admin_add_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form['#title'] = $this->t('Add language');
|
||||
|
||||
$predefined_languages = $this->languageManager->getStandardLanguageListWithoutConfigured();
|
||||
|
||||
$predefined_languages['custom'] = $this->t('Custom language...');
|
||||
$predefined_default = $form_state->getValue('predefined_langcode', key($predefined_languages));
|
||||
$form['predefined_langcode'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Language name'),
|
||||
'#default_value' => $predefined_default,
|
||||
'#options' => $predefined_languages,
|
||||
];
|
||||
$form['predefined_submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Add language'),
|
||||
'#name' => 'add_language',
|
||||
'#limit_validation_errors' => [['predefined_langcode'], ['predefined_submit']],
|
||||
'#states' => [
|
||||
'invisible' => [
|
||||
'select#edit-predefined-langcode' => ['value' => 'custom'],
|
||||
],
|
||||
],
|
||||
'#validate' => ['::validatePredefined'],
|
||||
'#submit' => ['::submitForm', '::save'],
|
||||
'#button_type' => 'primary',
|
||||
];
|
||||
|
||||
$custom_language_states_conditions = [
|
||||
'select#edit-predefined-langcode' => ['value' => 'custom'],
|
||||
];
|
||||
$form['custom_language'] = [
|
||||
'#type' => 'container',
|
||||
'#states' => [
|
||||
'visible' => $custom_language_states_conditions,
|
||||
],
|
||||
];
|
||||
$this->commonForm($form['custom_language']);
|
||||
$form['custom_language']['langcode']['#states'] = [
|
||||
'required' => $custom_language_states_conditions,
|
||||
];
|
||||
$form['custom_language']['label']['#states'] = [
|
||||
'required' => $custom_language_states_conditions,
|
||||
];
|
||||
$form['custom_language']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Add custom language'),
|
||||
'#name' => 'add_custom_language',
|
||||
'#validate' => ['::validateCustom'],
|
||||
'#submit' => ['::submitForm', '::save'],
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
parent::save($form, $form_state);
|
||||
|
||||
$t_args = ['%language' => $this->entity->label(), '%langcode' => $this->entity->id()];
|
||||
$this->logger('language')->notice('The %language (%langcode) language has been created.', $t_args);
|
||||
$this->messenger()->addStatus($this->t('The language %language has been created and can now be used.', $t_args));
|
||||
|
||||
if ($this->moduleHandler->moduleExists('block')) {
|
||||
// Tell the user they have the option to add a language switcher block
|
||||
// to their theme so they can switch between the languages.
|
||||
$this->messenger()->addStatus($this->t('Use one of the language switcher blocks to allow site visitors to switch between languages. You can enable these blocks on the <a href=":block-admin">block administration page</a>.', [':block-admin' => Url::fromRoute('block.admin_display')->toString()]));
|
||||
}
|
||||
$form_state->setRedirectUrl($this->entity->toUrl('collection'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function actions(array $form, FormStateInterface $form_state) {
|
||||
// No actions needed.
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the language addition form on custom language button.
|
||||
*/
|
||||
public function validateCustom(array $form, FormStateInterface $form_state) {
|
||||
if ($form_state->getValue('predefined_langcode') == 'custom') {
|
||||
$langcode = $form_state->getValue('langcode');
|
||||
// Reuse the editing form validation routine if we add a custom language.
|
||||
$this->validateCommon($form['custom_language'], $form_state);
|
||||
|
||||
if ($language = $this->languageManager->getLanguage($langcode)) {
|
||||
$form_state->setErrorByName('langcode', $this->t('The language %language (%langcode) already exists.', ['%language' => $language->getName(), '%langcode' => $langcode]));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$form_state->setErrorByName('predefined_langcode', $this->t('Use the <em>Add language</em> button to save a predefined language.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Element specific validator for the Add language button.
|
||||
*/
|
||||
public function validatePredefined($form, FormStateInterface $form_state) {
|
||||
$langcode = $form_state->getValue('predefined_langcode');
|
||||
if ($langcode == 'custom') {
|
||||
$form_state->setErrorByName('predefined_langcode', $this->t('Fill in the language details and save the language with <em>Add custom language</em>.'));
|
||||
}
|
||||
else {
|
||||
if ($language = $this->languageManager->getLanguage($langcode)) {
|
||||
$form_state->setErrorByName('predefined_langcode', $this->t('The language %language (%langcode) already exists.', ['%language' => $language->getName(), '%langcode' => $langcode]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
|
||||
$langcode = $form_state->getValue('predefined_langcode');
|
||||
if ($langcode == 'custom') {
|
||||
$langcode = $form_state->getValue('langcode');
|
||||
$label = $form_state->getValue('label');
|
||||
$direction = $form_state->getValue('direction');
|
||||
}
|
||||
else {
|
||||
$standard_languages = LanguageManager::getStandardLanguageList();
|
||||
$label = $standard_languages[$langcode][0];
|
||||
$direction = isset($standard_languages[$langcode][2]) ? $standard_languages[$langcode][2] : ConfigurableLanguage::DIRECTION_LTR;
|
||||
}
|
||||
$entity->set('id', $langcode);
|
||||
$entity->set('label', $label);
|
||||
$entity->set('direction', $direction);
|
||||
// There is no weight on the edit form. Fetch all configurable languages
|
||||
// ordered by weight and set the new language to be placed after them.
|
||||
$languages = \Drupal::languageManager()->getLanguages(ConfigurableLanguage::STATE_CONFIGURABLE);
|
||||
$last_language = end($languages);
|
||||
$entity->setWeight($last_language->getWeight() + 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityDeleteForm;
|
||||
|
||||
/**
|
||||
* Defines a confirmation form for deleting a language entity.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class LanguageDeleteForm extends EntityDeleteForm {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->t('Deleting a language will remove all interface translations associated with it, and content in this language will be set to be language neutral. This action cannot be undone.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'language_delete_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDeletionMessage() {
|
||||
return $this->t('The %language (%langcode) language has been removed.', ['%language' => $this->entity->label(), '%langcode' => $this->entity->id()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function logDeletionMessage() {
|
||||
$this->logger('language')->notice('The %language (%langcode) language has been removed.', ['%language' => $this->entity->label(), '%langcode' => $this->entity->id()]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Form;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Controller for language edit forms.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class LanguageEditForm extends LanguageFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
// @todo Remove in favor of base method.
|
||||
return 'language_admin_edit_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$this->commonForm($form);
|
||||
return parent::form($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save language'),
|
||||
'#validate' => ['::validateCommon'],
|
||||
'#submit' => ['::submitForm', '::save'],
|
||||
];
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
parent::save($form, $form_state);
|
||||
$form_state->setRedirectUrl($this->entity->toUrl('collection'));
|
||||
$this->logger('language')->notice('The %language (%langcode) language has been updated.', ['%language' => $this->entity->label(), '%langcode' => $this->entity->id()]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Form;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\language\ConfigurableLanguageManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Base form for language add and edit forms.
|
||||
*/
|
||||
abstract class LanguageFormBase extends EntityForm {
|
||||
|
||||
/**
|
||||
* The configurable language manager.
|
||||
*
|
||||
* @var \Drupal\language\ConfigurableLanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* Constructs a ContentEntityForm object.
|
||||
*
|
||||
* @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
|
||||
* The configurable language manager.
|
||||
*/
|
||||
public function __construct(ConfigurableLanguageManagerInterface $language_manager) {
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('language_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common elements of the language addition and editing form.
|
||||
*/
|
||||
public function commonForm(array &$form) {
|
||||
/* @var $language \Drupal\language\ConfigurableLanguageInterface */
|
||||
$language = $this->entity;
|
||||
if ($language->getId()) {
|
||||
$form['langcode_view'] = [
|
||||
'#type' => 'item',
|
||||
'#title' => $this->t('Language code'),
|
||||
'#markup' => $language->id(),
|
||||
];
|
||||
$form['langcode'] = [
|
||||
'#type' => 'value',
|
||||
'#value' => $language->id(),
|
||||
];
|
||||
}
|
||||
else {
|
||||
$form['langcode'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Language code'),
|
||||
'#maxlength' => 12,
|
||||
'#required' => TRUE,
|
||||
'#default_value' => '',
|
||||
'#disabled' => FALSE,
|
||||
'#description' => $this->t('Use language codes as <a href=":w3ctags">defined by the W3C</a> for interoperability. <em>Examples: "en", "en-gb" and "zh-hant".</em>', [':w3ctags' => 'http://www.w3.org/International/articles/language-tags/']),
|
||||
];
|
||||
}
|
||||
$form['label'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Language name'),
|
||||
'#maxlength' => 64,
|
||||
'#default_value' => $language->label(),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
$form['direction'] = [
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('Direction'),
|
||||
'#required' => TRUE,
|
||||
'#description' => $this->t('Direction that text in this language is presented.'),
|
||||
'#default_value' => $language->getDirection(),
|
||||
'#options' => [
|
||||
LanguageInterface::DIRECTION_LTR => $this->t('Left to right'),
|
||||
LanguageInterface::DIRECTION_RTL => $this->t('Right to left'),
|
||||
],
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the language editing element.
|
||||
*/
|
||||
public function validateCommon(array $form, FormStateInterface $form_state) {
|
||||
// Ensure sane field values for langcode and name.
|
||||
if (!isset($form['langcode_view']) && !preg_match('@^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$@', $form_state->getValue('langcode'))) {
|
||||
$form_state->setErrorByName('langcode', $this->t('%field must be a valid language tag as <a href=":url">defined by the W3C</a>.', [
|
||||
'%field' => $form['langcode']['#title'],
|
||||
':url' => 'http://www.w3.org/International/articles/language-tags/',
|
||||
]));
|
||||
}
|
||||
if ($form_state->getValue('label') != Html::escape($form_state->getValue('label'))) {
|
||||
$form_state->setErrorByName('label', $this->t('%field cannot contain any markup.', ['%field' => $form['label']['#title']]));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Form;
|
||||
|
||||
use Drupal\Core\Form\ConfigFormBaseTrait;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Defines a confirmation form for deleting a browser language negotiation mapping.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class NegotiationBrowserDeleteForm extends ConfirmFormBase {
|
||||
use ConfigFormBaseTrait;
|
||||
|
||||
/**
|
||||
* The browser language code to be deleted.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $browserLangcode;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['language.mappings'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to delete %browser_langcode?', ['%browser_langcode' => $this->browserLangcode]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('language.negotiation_browser');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'language_negotiation_configure_browser_delete_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $browser_langcode = NULL) {
|
||||
$this->browserLangcode = $browser_langcode;
|
||||
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->config('language.mappings')
|
||||
->clear('map.' . $this->browserLangcode)
|
||||
->save();
|
||||
|
||||
$args = [
|
||||
'%browser' => $this->browserLangcode,
|
||||
];
|
||||
|
||||
$this->logger('language')->notice('The browser language detection mapping for the %browser browser language code has been deleted.', $args);
|
||||
|
||||
$this->messenger()->addStatus($this->t('The mapping for the %browser browser language code has been deleted.', $args));
|
||||
|
||||
$form_state->setRedirect('language.negotiation_browser');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Form;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\language\ConfigurableLanguageManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Configure the browser language negotiation method for this site.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class NegotiationBrowserForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* The configurable language manager.
|
||||
*
|
||||
* @var \Drupal\language\ConfigurableLanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, ConfigurableLanguageManagerInterface $language_manager) {
|
||||
parent::__construct($config_factory);
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('config.factory'),
|
||||
$container->get('language_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'language_negotiation_configure_browser_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['language.mappings'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form = [];
|
||||
|
||||
// Initialize a language list to the ones available, including English.
|
||||
$languages = $this->languageManager->getLanguages();
|
||||
|
||||
$existing_languages = [];
|
||||
foreach ($languages as $langcode => $language) {
|
||||
$existing_languages[$langcode] = $language->getName();
|
||||
}
|
||||
|
||||
// If we have no languages available, present the list of predefined languages
|
||||
// only. If we do have already added languages, set up two option groups with
|
||||
// the list of existing and then predefined languages.
|
||||
if (empty($existing_languages)) {
|
||||
$language_options = $this->languageManager->getStandardLanguageListWithoutConfigured();
|
||||
}
|
||||
else {
|
||||
$language_options = [
|
||||
(string) $this->t('Existing languages') => $existing_languages,
|
||||
(string) $this->t('Languages not yet added') => $this->languageManager->getStandardLanguageListWithoutConfigured(),
|
||||
];
|
||||
}
|
||||
|
||||
$form['mappings'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => [
|
||||
$this->t('Browser language code'),
|
||||
$this->t('Site language'),
|
||||
$this->t('Operations'),
|
||||
],
|
||||
'#attributes' => ['id' => 'language-negotiation-browser'],
|
||||
'#empty' => $this->t('No browser language mappings available.'),
|
||||
];
|
||||
|
||||
$mappings = $this->language_get_browser_drupal_langcode_mappings();
|
||||
foreach ($mappings as $browser_langcode => $drupal_langcode) {
|
||||
$form['mappings'][$browser_langcode] = [
|
||||
'browser_langcode' => [
|
||||
'#title' => $this->t('Browser language code'),
|
||||
'#title_display' => 'invisible',
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => $browser_langcode,
|
||||
'#size' => 20,
|
||||
'#required' => TRUE,
|
||||
],
|
||||
'drupal_langcode' => [
|
||||
'#title' => $this->t('Site language'),
|
||||
'#title_display' => 'invisible',
|
||||
'#type' => 'select',
|
||||
'#options' => $language_options,
|
||||
'#default_value' => $drupal_langcode,
|
||||
'#required' => TRUE,
|
||||
],
|
||||
];
|
||||
// Operations column.
|
||||
$form['mappings'][$browser_langcode]['operations'] = [
|
||||
'#type' => 'operations',
|
||||
'#links' => [],
|
||||
];
|
||||
$form['mappings'][$browser_langcode]['operations']['#links']['delete'] = [
|
||||
'title' => $this->t('Delete'),
|
||||
'url' => Url::fromRoute('language.negotiation_browser_delete', ['browser_langcode' => $browser_langcode]),
|
||||
];
|
||||
}
|
||||
|
||||
// Add empty row.
|
||||
$form['new_mapping'] = [
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Add a new mapping'),
|
||||
'#tree' => TRUE,
|
||||
];
|
||||
$form['new_mapping']['browser_langcode'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Browser language code'),
|
||||
'#description' => $this->t('Use language codes as <a href=":w3ctags">defined by the W3C</a> for interoperability. <em>Examples: "en", "en-gb" and "zh-hant".</em>', [':w3ctags' => 'http://www.w3.org/International/articles/language-tags/']),
|
||||
'#size' => 20,
|
||||
];
|
||||
$form['new_mapping']['drupal_langcode'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Site language'),
|
||||
'#options' => $language_options,
|
||||
];
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
// Array to check if all browser language codes are unique.
|
||||
$unique_values = [];
|
||||
|
||||
// Check all mappings.
|
||||
if ($form_state->hasValue('mappings')) {
|
||||
$mappings = $form_state->getValue('mappings');
|
||||
foreach ($mappings as $key => $data) {
|
||||
// Make sure browser_langcode is unique.
|
||||
if (array_key_exists($data['browser_langcode'], $unique_values)) {
|
||||
$form_state->setErrorByName('mappings][new_mapping][browser_langcode', $this->t('Browser language codes must be unique.'));
|
||||
}
|
||||
elseif (preg_match('/[^a-z\-]/', $data['browser_langcode'])) {
|
||||
$form_state->setErrorByName('mappings][new_mapping][browser_langcode', $this->t('Browser language codes can only contain lowercase letters and a hyphen(-).'));
|
||||
}
|
||||
$unique_values[$data['browser_langcode']] = $data['drupal_langcode'];
|
||||
}
|
||||
}
|
||||
|
||||
// Check new mapping.
|
||||
$data = $form_state->getValue('new_mapping');
|
||||
if (!empty($data['browser_langcode'])) {
|
||||
// Make sure browser_langcode is unique.
|
||||
if (array_key_exists($data['browser_langcode'], $unique_values)) {
|
||||
$form_state->setErrorByName('mappings][' . $key . '][browser_langcode', $this->t('Browser language codes must be unique.'));
|
||||
}
|
||||
elseif (preg_match('/[^a-z\-]/', $data['browser_langcode'])) {
|
||||
$form_state->setErrorByName('mappings][' . $key . '][browser_langcode', $this->t('Browser language codes can only contain lowercase letters and a hyphen(-).'));
|
||||
}
|
||||
$unique_values[$data['browser_langcode']] = $data['drupal_langcode'];
|
||||
}
|
||||
|
||||
$form_state->set('mappings', $unique_values);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$mappings = $form_state->get('mappings');
|
||||
if (!empty($mappings)) {
|
||||
$config = $this->config('language.mappings');
|
||||
$config->setData(['map' => $mappings]);
|
||||
$config->save();
|
||||
}
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the browser's langcode mapping configuration array.
|
||||
*
|
||||
* @return array
|
||||
* The browser's langcode mapping configuration array.
|
||||
*/
|
||||
protected function language_get_browser_drupal_langcode_mappings() {
|
||||
$config = $this->config('language.mappings');
|
||||
if ($config->isNew()) {
|
||||
return [];
|
||||
}
|
||||
return $config->get('map');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Form;
|
||||
|
||||
use Drupal\Core\Block\BlockManagerInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\language\ConfigurableLanguageManagerInterface;
|
||||
use Drupal\language\LanguageNegotiatorInterface;
|
||||
use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSelected;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Configure the selected language negotiation method for this site.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class NegotiationConfigureForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* Stores the configuration object for language.types.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Config
|
||||
*/
|
||||
protected $languageTypes;
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\language\ConfigurableLanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The language negotiator.
|
||||
*
|
||||
* @var \Drupal\language\LanguageNegotiatorInterface
|
||||
*/
|
||||
protected $negotiator;
|
||||
|
||||
/**
|
||||
* The block manager.
|
||||
*
|
||||
* @var \Drupal\Core\Block\BlockManagerInterface
|
||||
*/
|
||||
protected $blockManager;
|
||||
|
||||
/**
|
||||
* The block storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface|null
|
||||
*/
|
||||
protected $blockStorage;
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* Constructs a NegotiationConfigureForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $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\Core\Block\BlockManagerInterface $block_manager
|
||||
* The block manager.
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $block_storage
|
||||
* The block storage, or NULL if not available.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, ConfigurableLanguageManagerInterface $language_manager, LanguageNegotiatorInterface $negotiator, BlockManagerInterface $block_manager, ThemeHandlerInterface $theme_handler, EntityStorageInterface $block_storage = NULL) {
|
||||
parent::__construct($config_factory);
|
||||
$this->languageTypes = $this->config('language.types');
|
||||
$this->languageManager = $language_manager;
|
||||
$this->negotiator = $negotiator;
|
||||
$this->blockManager = $block_manager;
|
||||
$this->themeHandler = $theme_handler;
|
||||
$this->blockStorage = $block_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
$entity_type_manager = $container->get('entity_type.manager');
|
||||
$block_storage = $entity_type_manager->hasHandler('block', 'storage') ? $entity_type_manager->getStorage('block') : NULL;
|
||||
return new static(
|
||||
$container->get('config.factory'),
|
||||
$container->get('language_manager'),
|
||||
$container->get('language_negotiator'),
|
||||
$container->get('plugin.manager.block'),
|
||||
$container->get('theme_handler'),
|
||||
$block_storage
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'language_negotiation_configure_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['language.types'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$configurable = $this->languageTypes->get('configurable');
|
||||
|
||||
$form = [
|
||||
'#theme' => 'language_negotiation_configure_form',
|
||||
'#language_types_info' => $this->languageManager->getDefinedLanguageTypesInfo(),
|
||||
'#language_negotiation_info' => $this->negotiator->getNegotiationMethods(),
|
||||
];
|
||||
$form['#language_types'] = [];
|
||||
|
||||
foreach ($form['#language_types_info'] as $type => $info) {
|
||||
// Show locked language types only if they are configurable.
|
||||
if (empty($info['locked']) || in_array($type, $configurable)) {
|
||||
$form['#language_types'][] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($form['#language_types'] as $type) {
|
||||
$this->configureFormTable($form, $type);
|
||||
}
|
||||
|
||||
$form['actions'] = ['#type' => 'actions'];
|
||||
$form['actions']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#button_type' => 'primary',
|
||||
'#value' => $this->t('Save settings'),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$configurable_types = $form['#language_types'];
|
||||
|
||||
$stored_values = $this->languageTypes->get('configurable');
|
||||
$customized = [];
|
||||
$method_weights_type = [];
|
||||
|
||||
foreach ($configurable_types as $type) {
|
||||
$customized[$type] = in_array($type, $stored_values);
|
||||
$method_weights = [];
|
||||
$enabled_methods = $form_state->getValue([$type, 'enabled']);
|
||||
$enabled_methods[LanguageNegotiationSelected::METHOD_ID] = TRUE;
|
||||
$method_weights_input = $form_state->getValue([$type, 'weight']);
|
||||
if ($form_state->hasValue([$type, 'configurable'])) {
|
||||
$customized[$type] = !$form_state->isValueEmpty([$type, 'configurable']);
|
||||
}
|
||||
|
||||
foreach ($method_weights_input as $method_id => $weight) {
|
||||
if ($enabled_methods[$method_id]) {
|
||||
$method_weights[$method_id] = $weight;
|
||||
}
|
||||
}
|
||||
|
||||
$method_weights_type[$type] = $method_weights;
|
||||
$this->languageTypes->set('negotiation.' . $type . '.method_weights', $method_weights_input)->save();
|
||||
}
|
||||
|
||||
// Update non-configurable language types and the related language
|
||||
// negotiation configuration.
|
||||
$this->negotiator->updateConfiguration(array_keys(array_filter($customized)));
|
||||
|
||||
// Update the language negotiations after setting the configurability.
|
||||
foreach ($method_weights_type as $type => $method_weights) {
|
||||
$this->negotiator->saveConfiguration($type, $method_weights);
|
||||
}
|
||||
|
||||
// Clear block definitions cache since the available blocks and their names
|
||||
// may have been changed based on the configurable types.
|
||||
if ($this->blockStorage) {
|
||||
// If there is an active language switcher for a language type that has
|
||||
// been made not configurable, deactivate it first.
|
||||
$non_configurable = array_keys(array_diff($customized, array_filter($customized)));
|
||||
$this->disableLanguageSwitcher($non_configurable);
|
||||
}
|
||||
$this->blockManager->clearCachedDefinitions();
|
||||
|
||||
$form_state->setRedirect('language.negotiation');
|
||||
$this->messenger()->addStatus($this->t('Language detection configuration saved.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a language negotiation method configuration table.
|
||||
*
|
||||
* @param array $form
|
||||
* The language negotiation configuration form.
|
||||
* @param string $type
|
||||
* The language type to generate the table for.
|
||||
*/
|
||||
protected function configureFormTable(array &$form, $type) {
|
||||
$info = $form['#language_types_info'][$type];
|
||||
|
||||
$table_form = [
|
||||
'#title' => $this->t('@type language detection', ['@type' => $info['name']]),
|
||||
'#tree' => TRUE,
|
||||
'#description' => $info['description'],
|
||||
'#language_negotiation_info' => [],
|
||||
'#show_operations' => FALSE,
|
||||
'weight' => ['#tree' => TRUE],
|
||||
];
|
||||
// Only show configurability checkbox for the unlocked language types.
|
||||
if (empty($info['locked'])) {
|
||||
$configurable = $this->languageTypes->get('configurable');
|
||||
$table_form['configurable'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Customize %language_name language detection to differ from Interface text language detection settings', ['%language_name' => $info['name']]),
|
||||
'#default_value' => in_array($type, $configurable),
|
||||
'#attributes' => ['class' => ['language-customization-checkbox']],
|
||||
'#attached' => [
|
||||
'library' => [
|
||||
'language/drupal.language.admin',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$negotiation_info = $form['#language_negotiation_info'];
|
||||
$enabled_methods = $this->languageTypes->get('negotiation.' . $type . '.enabled') ?: [];
|
||||
$methods_weight = $this->languageTypes->get('negotiation.' . $type . '.method_weights') ?: [];
|
||||
|
||||
// Add missing data to the methods lists.
|
||||
foreach ($negotiation_info as $method_id => $method) {
|
||||
if (!isset($methods_weight[$method_id])) {
|
||||
$methods_weight[$method_id] = isset($method['weight']) ? $method['weight'] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Order methods list by weight.
|
||||
asort($methods_weight);
|
||||
|
||||
foreach ($methods_weight as $method_id => $weight) {
|
||||
// A language method might be no more available if the defining module has
|
||||
// been disabled after the last configuration saving.
|
||||
if (!isset($negotiation_info[$method_id])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$enabled = isset($enabled_methods[$method_id]);
|
||||
$method = $negotiation_info[$method_id];
|
||||
|
||||
// List the method only if the current type is defined in its 'types' key.
|
||||
// If it is not defined default to all the configurable language types.
|
||||
$types = array_flip(isset($method['types']) ? $method['types'] : $form['#language_types']);
|
||||
|
||||
if (isset($types[$type])) {
|
||||
$table_form['#language_negotiation_info'][$method_id] = $method;
|
||||
$method_name = $method['name'];
|
||||
|
||||
$table_form['weight'][$method_id] = [
|
||||
'#type' => 'weight',
|
||||
'#title' => $this->t('Weight for @title language detection method', ['@title' => mb_strtolower($method_name)]),
|
||||
'#title_display' => 'invisible',
|
||||
'#default_value' => $weight,
|
||||
'#attributes' => ['class' => ["language-method-weight-$type"]],
|
||||
'#delta' => 20,
|
||||
];
|
||||
|
||||
$table_form['title'][$method_id] = ['#plain_text' => $method_name];
|
||||
|
||||
$table_form['enabled'][$method_id] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Enable @title language detection method', ['@title' => mb_strtolower($method_name)]),
|
||||
'#title_display' => 'invisible',
|
||||
'#default_value' => $enabled,
|
||||
];
|
||||
if ($method_id === LanguageNegotiationSelected::METHOD_ID) {
|
||||
$table_form['enabled'][$method_id]['#default_value'] = TRUE;
|
||||
$table_form['enabled'][$method_id]['#attributes'] = ['disabled' => 'disabled'];
|
||||
}
|
||||
|
||||
$table_form['description'][$method_id] = ['#markup' => $method['description']];
|
||||
|
||||
$config_op = [];
|
||||
if (isset($method['config_route_name'])) {
|
||||
$config_op['configure'] = [
|
||||
'title' => $this->t('Configure'),
|
||||
'url' => Url::fromRoute($method['config_route_name']),
|
||||
];
|
||||
// If there is at least one operation enabled show the operation
|
||||
// column.
|
||||
$table_form['#show_operations'] = TRUE;
|
||||
}
|
||||
$table_form['operation'][$method_id] = [
|
||||
'#type' => 'operations',
|
||||
'#links' => $config_op,
|
||||
];
|
||||
}
|
||||
}
|
||||
$form[$type] = $table_form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the language switcher blocks.
|
||||
*
|
||||
* @param array $language_types
|
||||
* An array containing all language types whose language switchers need to
|
||||
* be disabled.
|
||||
*/
|
||||
protected function disableLanguageSwitcher(array $language_types) {
|
||||
$theme = $this->themeHandler->getDefault();
|
||||
$blocks = $this->blockStorage->loadByProperties(['theme' => $theme]);
|
||||
foreach ($language_types as $language_type) {
|
||||
foreach ($blocks as $block) {
|
||||
if ($block->getPluginId() == 'language_block:' . $language_type) {
|
||||
$block->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Form;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
|
||||
/**
|
||||
* Configure the selected language negotiation method for this site.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class NegotiationSelectedForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'language_negotiation_configure_selected_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['language.negotiation'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$config = $this->config('language.negotiation');
|
||||
$form['selected_langcode'] = [
|
||||
'#type' => 'language_select',
|
||||
'#title' => $this->t('Language'),
|
||||
'#languages' => LanguageInterface::STATE_CONFIGURABLE | LanguageInterface::STATE_SITE_DEFAULT,
|
||||
'#default_value' => $config->get('selected_langcode'),
|
||||
];
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->config('language.negotiation')
|
||||
->set('selected_langcode', $form_state->getValue('selected_langcode'))
|
||||
->save();
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Form;
|
||||
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Configure the session language negotiation method for this site.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class NegotiationSessionForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'language_negotiation_configure_session_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['language.negotiation'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$config = $this->config('language.negotiation');
|
||||
$form['language_negotiation_session_param'] = [
|
||||
'#title' => $this->t('Request/session parameter'),
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => $config->get('session.parameter'),
|
||||
'#description' => $this->t('Name of the request/session parameter used to determine the desired language.'),
|
||||
];
|
||||
|
||||
$form_state->setRedirect('language.negotiation');
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->config('language.negotiation')
|
||||
->set('session.parameter', $form_state->getValue('language_negotiation_session_param'))
|
||||
->save();
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Form;
|
||||
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
|
||||
|
||||
/**
|
||||
* Configure the URL language negotiation method for this site.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class NegotiationUrlForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* Constructs a new NegotiationUrlForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The factory for configuration objects.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, LanguageManagerInterface $language_manager) {
|
||||
parent::__construct($config_factory);
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('config.factory'),
|
||||
$container->get('language_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'language_negotiation_configure_url_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['language.negotiation'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
global $base_url;
|
||||
$config = $this->config('language.negotiation');
|
||||
|
||||
$form['language_negotiation_url_part'] = [
|
||||
'#title' => $this->t('Part of the URL that determines language'),
|
||||
'#type' => 'radios',
|
||||
'#options' => [
|
||||
LanguageNegotiationUrl::CONFIG_PATH_PREFIX => $this->t('Path prefix'),
|
||||
LanguageNegotiationUrl::CONFIG_DOMAIN => $this->t('Domain'),
|
||||
],
|
||||
'#default_value' => $config->get('url.source'),
|
||||
];
|
||||
|
||||
$form['prefix'] = [
|
||||
'#type' => 'details',
|
||||
'#tree' => TRUE,
|
||||
'#title' => $this->t('Path prefix configuration'),
|
||||
'#open' => TRUE,
|
||||
'#description' => $this->t('Language codes or other custom text to use as a path prefix for URL language detection. For the selected fallback language, this value may be left blank. <strong>Modifying this value may break existing URLs. Use with caution in a production environment.</strong> Example: Specifying "deutsch" as the path prefix code for German results in URLs like "example.com/deutsch/contact".'),
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
':input[name="language_negotiation_url_part"]' => [
|
||||
'value' => (string) LanguageNegotiationUrl::CONFIG_PATH_PREFIX,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
$form['domain'] = [
|
||||
'#type' => 'details',
|
||||
'#tree' => TRUE,
|
||||
'#title' => $this->t('Domain configuration'),
|
||||
'#open' => TRUE,
|
||||
'#description' => $this->t('The domain names to use for these languages. <strong>Modifying this value may break existing URLs. Use with caution in a production environment.</strong> Example: Specifying "de.example.com" as language domain for German will result in a URL like "http://de.example.com/contact".'),
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
':input[name="language_negotiation_url_part"]' => [
|
||||
'value' => (string) LanguageNegotiationUrl::CONFIG_DOMAIN,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$languages = $this->languageManager->getLanguages();
|
||||
$prefixes = $config->get('url.prefixes');
|
||||
$domains = $config->get('url.domains');
|
||||
foreach ($languages as $langcode => $language) {
|
||||
$t_args = ['%language' => $language->getName(), '%langcode' => $language->getId()];
|
||||
$form['prefix'][$langcode] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $language->isDefault() ? $this->t('%language (%langcode) path prefix (Default language)', $t_args) : $this->t('%language (%langcode) path prefix', $t_args),
|
||||
'#maxlength' => 64,
|
||||
'#default_value' => isset($prefixes[$langcode]) ? $prefixes[$langcode] : '',
|
||||
'#field_prefix' => $base_url . '/',
|
||||
];
|
||||
$form['domain'][$langcode] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('%language (%langcode) domain', ['%language' => $language->getName(), '%langcode' => $language->getId()]),
|
||||
'#maxlength' => 128,
|
||||
'#default_value' => isset($domains[$langcode]) ? $domains[$langcode] : '',
|
||||
];
|
||||
}
|
||||
|
||||
$form_state->setRedirect('language.negotiation');
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
$languages = $this->languageManager->getLanguages();
|
||||
|
||||
// Count repeated values for uniqueness check.
|
||||
$count = array_count_values($form_state->getValue('prefix'));
|
||||
$default_langcode = $this->config('language.negotiation')->get('selected_langcode');
|
||||
if ($default_langcode == LanguageInterface::LANGCODE_SITE_DEFAULT) {
|
||||
$default_langcode = $this->languageManager->getDefaultLanguage()->getId();
|
||||
}
|
||||
foreach ($languages as $langcode => $language) {
|
||||
$value = $form_state->getValue(['prefix', $langcode]);
|
||||
if ($value === '') {
|
||||
if (!($default_langcode == $langcode) && $form_state->getValue('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.
|
||||
$form_state->setErrorByName("prefix][$langcode", $this->t('The prefix may only be left blank for the <a href=":url">selected detection fallback language.</a>', [
|
||||
':url' => Url::fromRoute('language.negotiation_selected')->toString(),
|
||||
]));
|
||||
}
|
||||
}
|
||||
elseif (strpos($value, '/') !== FALSE) {
|
||||
// Throw a form error if the string contains a slash,
|
||||
// which would not work.
|
||||
$form_state->setErrorByName("prefix][$langcode", $this->t('The prefix may not contain a slash.'));
|
||||
}
|
||||
elseif (isset($count[$value]) && $count[$value] > 1) {
|
||||
// Throw a form error if there are two languages with the same
|
||||
// domain/prefix.
|
||||
$form_state->setErrorByName("prefix][$langcode", $this->t('The prefix for %language, %value, is not unique.', ['%language' => $language->getName(), '%value' => $value]));
|
||||
}
|
||||
}
|
||||
|
||||
// Count repeated values for uniqueness check.
|
||||
$count = array_count_values($form_state->getValue('domain'));
|
||||
foreach ($languages as $langcode => $language) {
|
||||
$value = $form_state->getValue(['domain', $langcode]);
|
||||
|
||||
if ($value === '') {
|
||||
if ($form_state->getValue('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.
|
||||
$form_state->setErrorByName("domain][$langcode", $this->t('The domain may not be left blank for %language.', ['%language' => $language->getName()]));
|
||||
}
|
||||
}
|
||||
elseif (isset($count[$value]) && $count[$value] > 1) {
|
||||
// Throw a form error if there are two languages with the same
|
||||
// domain/domain.
|
||||
$form_state->setErrorByName("domain][$langcode", $this->t('The domain for %language, %value, is not unique.', ['%language' => $language->getName(), '%value' => $value]));
|
||||
}
|
||||
}
|
||||
|
||||
// Domain names should not contain protocol and/or ports.
|
||||
foreach ($languages as $langcode => $language) {
|
||||
$value = $form_state->getValue(['domain', $langcode]);
|
||||
if (!empty($value)) {
|
||||
// Ensure we have exactly one protocol when checking the hostname.
|
||||
$host = 'http://' . str_replace(['http://', 'https://'], '', $value);
|
||||
if (parse_url($host, PHP_URL_HOST) != $value) {
|
||||
$form_state->setErrorByName("domain][$langcode", $this->t('The domain for %language may only contain the domain name, not a trailing slash, protocol and/or port.', ['%language' => $language->getName()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parent::validateForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// Save selected format (prefix or domain).
|
||||
$this->config('language.negotiation')
|
||||
->set('url.source', $form_state->getValue('language_negotiation_url_part'))
|
||||
// Save new domain and prefix values.
|
||||
->set('url.prefixes', $form_state->getValue('prefix'))
|
||||
->set('url.domains', $form_state->getValue('domain'))
|
||||
->save();
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\HttpKernel;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
|
||||
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\language\ConfigurableLanguageManagerInterface;
|
||||
use Drupal\language\EventSubscriber\ConfigSubscriber;
|
||||
use Drupal\language\LanguageNegotiatorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Processes the inbound path using path alias lookups.
|
||||
*/
|
||||
class PathProcessorLanguage implements InboundPathProcessorInterface, OutboundPathProcessorInterface {
|
||||
|
||||
/**
|
||||
* A config factory for retrieving required config settings.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Language manager for retrieving the url language type.
|
||||
*
|
||||
* @var \Drupal\language\ConfigurableLanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The language negotiator.
|
||||
*
|
||||
* @var \Drupal\language\LanguageNegotiatorInterface
|
||||
*/
|
||||
protected $negotiator;
|
||||
|
||||
/**
|
||||
* Local cache for language path processors.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $processors;
|
||||
|
||||
/**
|
||||
* Flag indicating whether the site is multilingual.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $multilingual;
|
||||
|
||||
/**
|
||||
* The language configuration event subscriber.
|
||||
*
|
||||
* @var \Drupal\language\EventSubscriber\ConfigSubscriber
|
||||
*/
|
||||
protected $configSubscriber;
|
||||
|
||||
/**
|
||||
* Constructs a PathProcessorLanguage object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config
|
||||
* A config factory object for retrieving configuration settings.
|
||||
* @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
|
||||
* The configurable language manager.
|
||||
* @param \Drupal\language\LanguageNegotiatorInterface $negotiator
|
||||
* The language negotiator.
|
||||
* @param \Drupal\Core\Session\AccountInterface $current_user
|
||||
* The current active user.
|
||||
* @param \Drupal\language\EventSubscriber\ConfigSubscriber $config_subscriber
|
||||
* The language configuration event subscriber.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config, ConfigurableLanguageManagerInterface $language_manager, LanguageNegotiatorInterface $negotiator, AccountInterface $current_user, ConfigSubscriber $config_subscriber) {
|
||||
$this->config = $config;
|
||||
$this->languageManager = $language_manager;
|
||||
$this->negotiator = $negotiator;
|
||||
$this->negotiator->setCurrentUser($current_user);
|
||||
$this->configSubscriber = $config_subscriber;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processInbound($path, Request $request) {
|
||||
if (!empty($path)) {
|
||||
$scope = 'inbound';
|
||||
if (!isset($this->processors[$scope])) {
|
||||
$this->initProcessors($scope);
|
||||
}
|
||||
foreach ($this->processors[$scope] as $instance) {
|
||||
$path = $instance->processInbound($path, $request);
|
||||
}
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
|
||||
if (!isset($this->multilingual)) {
|
||||
$this->multilingual = $this->languageManager->isMultilingual();
|
||||
}
|
||||
if ($this->multilingual) {
|
||||
$this->negotiator->reset();
|
||||
$scope = 'outbound';
|
||||
if (!isset($this->processors[$scope])) {
|
||||
$this->initProcessors($scope);
|
||||
}
|
||||
foreach ($this->processors[$scope] as $instance) {
|
||||
$path = $instance->processOutbound($path, $options, $request, $bubbleable_metadata);
|
||||
}
|
||||
// No language dependent path allowed in this mode.
|
||||
if (empty($this->processors[$scope])) {
|
||||
unset($options['language']);
|
||||
}
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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] = [];
|
||||
$weights = [];
|
||||
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);
|
||||
$weights[$method_id] = $method['weight'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the processors list, so that their functions are called in the
|
||||
// order specified by the weight of the methods.
|
||||
uksort($this->processors[$scope], function ($method_id_a, $method_id_b) use ($weights) {
|
||||
$a_weight = $weights[$method_id_a];
|
||||
$b_weight = $weights[$method_id_b];
|
||||
|
||||
if ($a_weight == $b_weight) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ($a_weight < $b_weight) ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the injected event subscriber with the language path processor.
|
||||
*
|
||||
* The language path processor service is registered only on multilingual
|
||||
* site configuration, thus we inject it in the event subscriber only when
|
||||
* it is initialized.
|
||||
*/
|
||||
public function initConfigSubscriber() {
|
||||
$this->configSubscriber->setPathProcessorLanguage($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the collected processors instances.
|
||||
*/
|
||||
public function reset() {
|
||||
$this->processors = [];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Defines the access control handler for the language entity type.
|
||||
*
|
||||
* @see \Drupal\language\Entity\ConfigurableLanguage
|
||||
*/
|
||||
class LanguageAccessControlHandler extends EntityAccessControlHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
|
||||
switch ($operation) {
|
||||
case 'view':
|
||||
return parent::checkAccess($entity, $operation, $account);
|
||||
|
||||
case 'update':
|
||||
/* @var \Drupal\Core\Language\LanguageInterface $entity */
|
||||
return AccessResult::allowedIf(!$entity->isLocked())->addCacheableDependency($entity)
|
||||
->andIf(parent::checkAccess($entity, $operation, $account));
|
||||
|
||||
case 'delete':
|
||||
/* @var \Drupal\Core\Language\LanguageInterface $entity */
|
||||
return AccessResult::allowedIf(!$entity->isLocked())->addCacheableDependency($entity)
|
||||
->andIf(AccessResult::allowedIf(!$entity->isDefault())->addCacheableDependency($entity))
|
||||
->andIf(parent::checkAccess($entity, $operation, $account));
|
||||
|
||||
default:
|
||||
// No opinion.
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\ParamConverter\ParamConverterInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Converts parameters for upcasting entity IDs to full objects.
|
||||
*/
|
||||
class LanguageConverter implements ParamConverterInterface {
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* Constructs a new LanguageConverter.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
*/
|
||||
public function __construct(LanguageManagerInterface $language_manager) {
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function convert($value, $definition, $name, array $defaults) {
|
||||
if (!empty($value)) {
|
||||
return $this->languageManager->getLanguage($value);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies($definition, $name, Route $route) {
|
||||
return (!empty($definition['type']) && $definition['type'] == 'language');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Config\Entity\DraggableListBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Messenger\MessengerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a class to build a listing of language entities.
|
||||
*
|
||||
* @see \Drupal\language\Entity\ConfigurableLanguage
|
||||
*/
|
||||
class LanguageListBuilder extends DraggableListBuilder {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $entitiesKey = 'languages';
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The configuration factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The messenger.
|
||||
*
|
||||
* @var \Drupal\Core\Messenger\MessengerInterface
|
||||
*/
|
||||
protected $messenger;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('entity_type.manager')->getStorage($entity_type->id()),
|
||||
$container->get('language_manager'),
|
||||
$container->get('config.factory'),
|
||||
$container->get('messenger')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new LanguageListBuilder object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage handler class.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The factory for configuration objects.
|
||||
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
|
||||
* The messenger.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, LanguageManagerInterface $language_manager, ConfigFactoryInterface $config_factory, MessengerInterface $messenger) {
|
||||
parent::__construct($entity_type, $storage);
|
||||
$this->languageManager = $language_manager;
|
||||
$this->configFactory = $config_factory;
|
||||
$this->messenger = $messenger;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load() {
|
||||
$entities = $this->storage->loadByProperties(['locked' => FALSE]);
|
||||
|
||||
// Sort the entities using the entity class's sort() method.
|
||||
// See \Drupal\Core\Config\Entity\ConfigEntityBase::sort().
|
||||
uasort($entities, [$this->entityType->getClass(), 'sort']);
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'language_admin_overview_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header = [
|
||||
'label' => t('Name'),
|
||||
'default' => t('Default'),
|
||||
] + parent::buildHeader();
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
$row['label'] = $entity->label();
|
||||
$row['default'] = [
|
||||
'#type' => 'radio',
|
||||
'#parents' => ['site_default_language'],
|
||||
'#title' => t('Set @title as default', ['@title' => $entity->label()]),
|
||||
'#title_display' => 'invisible',
|
||||
'#return_value' => $entity->id(),
|
||||
'#id' => 'edit-site-default-language-' . $entity->id(),
|
||||
];
|
||||
// Mark the right language as default in the form.
|
||||
if ($entity->id() == $this->languageManager->getDefaultLanguage()->getId()) {
|
||||
$row['default']['#default_value'] = $entity->id();
|
||||
}
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
|
||||
$form[$this->entitiesKey]['#languages'] = $this->entities;
|
||||
$form['actions']['submit']['#value'] = t('Save configuration');
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
if (!isset($this->entities[$form_state->getValue('site_default_language')])) {
|
||||
$form_state->setErrorByName('site_default_language', $this->t('Selected default language no longer exists.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
|
||||
// Save the default language if changed.
|
||||
$new_id = $form_state->getValue('site_default_language');
|
||||
if ($new_id != $this->languageManager->getDefaultLanguage()->getId()) {
|
||||
$this->configFactory->getEditable('system.site')->set('default_langcode', $new_id)->save();
|
||||
$this->languageManager->reset();
|
||||
}
|
||||
|
||||
if ($this->languageManager instanceof ConfigurableLanguageManagerInterface) {
|
||||
$this->languageManager->updateLockedLanguageWeights();
|
||||
}
|
||||
|
||||
$this->messenger->addStatus($this->t('Configuration saved.'));
|
||||
// Force the redirection to the page with the language we have just
|
||||
// selected as default.
|
||||
$form_state->setRedirectUrl($this->entities[$new_id]->toUrl('collection', ['language' => $this->entities[$new_id]]));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Base class for language negotiation methods.
|
||||
*/
|
||||
abstract class LanguageNegotiationMethodBase implements LanguageNegotiationMethodInterface {
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The configuration factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* The current active user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setLanguageManager(ConfigurableLanguageManagerInterface $language_manager) {
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setConfig(ConfigFactoryInterface $config) {
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCurrentUser(AccountInterface $current_user) {
|
||||
$this->currentUser = $current_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function persist(LanguageInterface $language) {
|
||||
// Default implementation persists nothing.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Interface for language negotiation classes.
|
||||
*/
|
||||
interface LanguageNegotiationMethodInterface {
|
||||
|
||||
/**
|
||||
* Injects the language manager.
|
||||
*
|
||||
* @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
|
||||
* The language manager to be used to retrieve the language list and the
|
||||
* already negotiated languages.
|
||||
*/
|
||||
public function setLanguageManager(ConfigurableLanguageManagerInterface $language_manager);
|
||||
|
||||
/**
|
||||
* Injects the configuration factory.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config
|
||||
* The configuration factory.
|
||||
*/
|
||||
public function setConfig(ConfigFactoryInterface $config);
|
||||
|
||||
/**
|
||||
* Injects the current user.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $current_user
|
||||
* The current active user.
|
||||
*/
|
||||
public function setCurrentUser(AccountInterface $current_user);
|
||||
|
||||
/**
|
||||
* Performs language negotiation.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* (optional) The current request. Defaults to NULL if it has not been
|
||||
* initialized yet.
|
||||
*
|
||||
* @return string
|
||||
* A valid language code or FALSE if the negotiation was unsuccessful.
|
||||
*/
|
||||
public function getLangcode(Request $request = NULL);
|
||||
|
||||
/**
|
||||
* Notifies the plugin that the language code it returned has been accepted.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageInterface $language
|
||||
* The accepted language.
|
||||
*/
|
||||
public function persist(LanguageInterface $language);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
|
||||
/**
|
||||
* Manages language negotiation methods.
|
||||
*/
|
||||
class LanguageNegotiationMethodManager extends DefaultPluginManager {
|
||||
|
||||
/**
|
||||
* Constructs a new LanguageNegotiationMethodManager object.
|
||||
*
|
||||
* @param \Traversable $namespaces
|
||||
* An object that implements \Traversable which contains the root paths
|
||||
* keyed by the corresponding namespace to look for plugin implementations.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* An object that implements CacheBackendInterface
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* An object that implements ModuleHandlerInterface
|
||||
*/
|
||||
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
|
||||
parent::__construct('Plugin/LanguageNegotiation', $namespaces, $module_handler, 'Drupal\language\LanguageNegotiationMethodInterface', 'Drupal\language\Annotation\LanguageNegotiation');
|
||||
$this->cacheBackend = $cache_backend;
|
||||
$this->cacheKeyPrefix = 'language_negotiation_plugins';
|
||||
$this->cacheKey = 'language_negotiation_plugins';
|
||||
$this->alterInfo('language_negotiation_info');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUI;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* Class responsible for performing language negotiation.
|
||||
*/
|
||||
class LanguageNegotiator implements LanguageNegotiatorInterface {
|
||||
|
||||
/**
|
||||
* The language negotiation method plugin manager.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\PluginManagerInterface
|
||||
*/
|
||||
protected $negotiatorManager;
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\language\ConfigurableLanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The configuration factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The settings instance.
|
||||
*
|
||||
* @var \Drupal\Core\Site\Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* The request stack object.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\RequestStack
|
||||
*/
|
||||
protected $requestStack;
|
||||
|
||||
/**
|
||||
* The current active user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* Local cache for language negotiation method instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $methods;
|
||||
|
||||
/**
|
||||
* An array of language objects keyed by method id.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageInterface[]
|
||||
*/
|
||||
protected $negotiatedLanguages = [];
|
||||
|
||||
/**
|
||||
* Constructs a new LanguageNegotiator object.
|
||||
*
|
||||
* @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Component\Plugin\PluginManagerInterface $negotiator_manager
|
||||
* The language negotiation methods plugin manager
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The configuration factory.
|
||||
* @param \Drupal\Core\Site\Settings $settings
|
||||
* The settings instance.
|
||||
*/
|
||||
public function __construct(ConfigurableLanguageManagerInterface $language_manager, PluginManagerInterface $negotiator_manager, ConfigFactoryInterface $config_factory, Settings $settings, RequestStack $requestStack) {
|
||||
$this->languageManager = $language_manager;
|
||||
$this->negotiatorManager = $negotiator_manager;
|
||||
$this->configFactory = $config_factory;
|
||||
$this->settings = $settings;
|
||||
$this->requestStack = $requestStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
$this->methods = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCurrentUser(AccountInterface $current_user) {
|
||||
$this->currentUser = $current_user;
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function initializeType($type) {
|
||||
$language = NULL;
|
||||
|
||||
if ($this->currentUser) {
|
||||
// Execute the language negotiation methods in the order they were set up
|
||||
// and return the first valid language found.
|
||||
foreach ($this->getEnabledNegotiators($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]) : NULL;
|
||||
|
||||
if ($language) {
|
||||
$this->getNegotiationMethodInstance($method_id)->persist($language);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$language) {
|
||||
// If no other language was found use the default one.
|
||||
$language = $this->languageManager->getDefaultLanguage();
|
||||
$method_id = static::METHOD_ID;
|
||||
}
|
||||
|
||||
return [$method_id => $language];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets enabled detection methods for the provided language type.
|
||||
*
|
||||
* @param string $type
|
||||
* The language type.
|
||||
*
|
||||
* @return array
|
||||
* An array of enabled detection methods for the provided language type.
|
||||
*/
|
||||
protected function getEnabledNegotiators($type) {
|
||||
return $this->configFactory->get('language.types')->get('negotiation.' . $type . '.enabled') ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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\LanguageInterface|null
|
||||
* Negotiated language object for given type and method, FALSE otherwise.
|
||||
*/
|
||||
protected function negotiateLanguage($type, $method_id) {
|
||||
$langcode = NULL;
|
||||
$method = $this->negotiatorManager->getDefinition($method_id);
|
||||
|
||||
if (!isset($method['types']) || in_array($type, $method['types'])) {
|
||||
$langcode = $this->getNegotiationMethodInstance($method_id)->getLangcode($this->requestStack->getCurrentRequest());
|
||||
}
|
||||
|
||||
$languages = $this->languageManager->getLanguages();
|
||||
return isset($languages[$langcode]) ? $languages[$langcode] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNegotiationMethods($type = NULL) {
|
||||
$definitions = $this->negotiatorManager->getDefinitions();
|
||||
if (isset($type)) {
|
||||
$enabled_methods = $this->getEnabledNegotiators($type);
|
||||
$definitions = array_intersect_key($definitions, $enabled_methods);
|
||||
}
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNegotiationMethodInstance($method_id) {
|
||||
if (!isset($this->methods[$method_id])) {
|
||||
$instance = $this->negotiatorManager->createInstance($method_id, []);
|
||||
$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) {
|
||||
$enabled_methods = $this->getEnabledNegotiators($type);
|
||||
return empty($enabled_methods) ? LanguageNegotiatorInterface::METHOD_ID : key($enabled_methods);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isNegotiationMethodEnabled($method_id, $type = NULL) {
|
||||
$enabled = FALSE;
|
||||
$language_types = !empty($type) ? [$type] : $this->languageManager->getLanguageTypes();
|
||||
|
||||
foreach ($language_types as $type) {
|
||||
$enabled_methods = $this->getEnabledNegotiators($type);
|
||||
if (isset($enabled_methods[$method_id])) {
|
||||
$enabled = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveConfiguration($type, $enabled_methods) {
|
||||
// As configurable language types might have changed, we reset the cache.
|
||||
$this->languageManager->reset();
|
||||
$definitions = $this->getNegotiationMethods();
|
||||
$default_types = $this->languageManager->getLanguageTypes();
|
||||
|
||||
// Ensure that the weights are integers.
|
||||
$enabled_methods = array_map('intval', $enabled_methods);
|
||||
|
||||
// Order the language negotiation method list by weight.
|
||||
asort($enabled_methods);
|
||||
foreach ($enabled_methods 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($enabled_methods[$method_id]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
unset($enabled_methods[$method_id]);
|
||||
}
|
||||
}
|
||||
$this->configFactory->getEditable('language.types')->set('negotiation.' . $type . '.enabled', $enabled_methods)->save(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function purgeConfiguration() {
|
||||
// Ensure that we are getting the defined language negotiation information.
|
||||
// An invocation of \Drupal\Core\Extension\ModuleInstaller::install() or
|
||||
// \Drupal\Core\Extension\ModuleInstaller::uninstall() could invalidate the
|
||||
// cached information.
|
||||
$this->negotiatorManager->clearCachedDefinitions();
|
||||
$this->languageManager->reset();
|
||||
foreach ($this->languageManager->getDefinedLanguageTypesInfo() as $type => $info) {
|
||||
$this->saveConfiguration($type, $this->getEnabledNegotiators($type));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateConfiguration(array $types) {
|
||||
// Ensure that we are getting the defined language negotiation information.
|
||||
// An invocation of \Drupal\Core\Extension\ModuleInstaller::install() or
|
||||
// \Drupal\Core\Extension\ModuleInstaller::uninstall() could invalidate the
|
||||
// cached information.
|
||||
$this->negotiatorManager->clearCachedDefinitions();
|
||||
$this->languageManager->reset();
|
||||
|
||||
$language_types = [];
|
||||
$language_types_info = $this->languageManager->getDefinedLanguageTypesInfo();
|
||||
$method_definitions = $this->getNegotiationMethods();
|
||||
|
||||
foreach ($language_types_info as $type => $info) {
|
||||
$configurable = in_array($type, $types);
|
||||
|
||||
// The default language negotiation settings, if available, are stored in
|
||||
// $info['fixed'].
|
||||
$has_default_settings = !empty($info['fixed']);
|
||||
// Check whether the language type is unlocked. Only the status of
|
||||
// unlocked language types can be toggled between configurable and
|
||||
// non-configurable.
|
||||
if (empty($info['locked'])) {
|
||||
if (!$configurable && !$has_default_settings) {
|
||||
// If we have an unlocked non-configurable language type without
|
||||
// default language negotiation settings, we use the values
|
||||
// negotiated for the interface language which, should always be
|
||||
// available.
|
||||
$method_weights = [LanguageNegotiationUI::METHOD_ID];
|
||||
$method_weights = array_flip($method_weights);
|
||||
$this->saveConfiguration($type, $method_weights);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The language type is locked. 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.
|
||||
|
||||
// If the language type is locked we can just store its default language
|
||||
// negotiation settings if it has some, since it is not configurable.
|
||||
if ($has_default_settings) {
|
||||
$method_weights = [];
|
||||
// Default settings are in $info['fixed'].
|
||||
|
||||
foreach ($info['fixed'] as $weight => $method_id) {
|
||||
if (isset($method_definitions[$method_id])) {
|
||||
$method_weights[$method_id] = $weight;
|
||||
}
|
||||
}
|
||||
$this->saveConfiguration($type, $method_weights);
|
||||
}
|
||||
else {
|
||||
// It was missing default settings, so force it to be configurable.
|
||||
$configurable = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Accumulate information for each language type so it can be saved later.
|
||||
$language_types[$type] = $configurable;
|
||||
}
|
||||
|
||||
// Store the language type configuration.
|
||||
$config = [
|
||||
'configurable' => array_keys(array_filter($language_types)),
|
||||
'all' => array_keys($language_types),
|
||||
];
|
||||
$this->languageManager->saveLanguageTypesConfiguration($config);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Common interface for language negotiation services.
|
||||
*
|
||||
* The language negotiation API is based on two major concepts:
|
||||
* - Language types: types of translatable data (the types of data that a user
|
||||
* can view or request).
|
||||
* - Language negotiation methods: responsible for determining which language to
|
||||
* use to present a particular piece of data to the user.
|
||||
* Both language types and language negotiation methods are customizable.
|
||||
*
|
||||
* Drupal defines three built-in language types:
|
||||
* - Interface language: The page's main language, used to present translated
|
||||
* user interface elements such as titles, labels, help text, and messages.
|
||||
* - Content language: The language used to present content that is available
|
||||
* in more than one language.
|
||||
* - URL language: The language associated with URLs. When generating a URL,
|
||||
* this value will be used for URL's as a default if no explicit preference is
|
||||
* provided.
|
||||
* Modules can define additional language types through
|
||||
* hook_language_types_info(), and alter existing language type definitions
|
||||
* through hook_language_types_info_alter().
|
||||
*
|
||||
* Language types may be configurable or fixed. The language negotiation
|
||||
* methods associated with a configurable language type can be explicitly
|
||||
* set through the user interface. A fixed language type has predetermined
|
||||
* (module-defined) language negotiation settings and, thus, does not appear in
|
||||
* the configuration page. Here is a code snippet that makes the content
|
||||
* language (which by default inherits the interface language's values)
|
||||
* configurable:
|
||||
* @code
|
||||
* function mymodule_language_types_info_alter(&$language_types) {
|
||||
* unset($language_types[LanguageInterface::TYPE_CONTENT]['fixed']);
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* The locked configuration property prevents one language type from being
|
||||
* switched from customized to not customized, and vice versa.
|
||||
* @see \Drupal\language\LanguageNegotiator::updateConfiguration()
|
||||
*
|
||||
* Every language type can have a different set of language negotiation methods
|
||||
* assigned to it. Different language types often share the same language
|
||||
* negotiation settings, but they can have independent settings if needed. If
|
||||
* two language types are configured the same way, their language switcher
|
||||
* configuration will be functionally identical and the same settings will act
|
||||
* on both language types.
|
||||
*
|
||||
* Drupal defines the following built-in language negotiation methods:
|
||||
* - URL: Determine the language from the URL (path prefix or domain).
|
||||
* - Session: Determine the language from a request/session parameter.
|
||||
* - User: Follow the user's language preference.
|
||||
* - User admin language: Identify admin language from the user preferences.
|
||||
* - Browser: Determine the language from the browser's language settings.
|
||||
* - Selected language: Use the default site language.
|
||||
* Language negotiation methods are simple plugin classes that implement a
|
||||
* particular logic to return a language code. For instance, the URL method
|
||||
* searches for a valid path prefix or domain name in the current request URL.
|
||||
* If a language negotiation method does not return a valid language code, the
|
||||
* next method associated with the language type (based on method weight) is
|
||||
* invoked.
|
||||
*
|
||||
* Modules can define additional language negotiation methods by simply provide
|
||||
* the related plugins, and alter existing methods through
|
||||
* hook_language_negotiation_info_alter(). Here is an example snippet that lets
|
||||
* path prefixes be ignored for administrative paths:
|
||||
* @code
|
||||
* function mymodule_language_negotiation_info_alter(&$negotiation_info) {
|
||||
* // Replace the original plugin with our own implementation.
|
||||
* $method_id = \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl::METHOD_ID;
|
||||
* $negotiation_info[$method_id]['class'] = 'Drupal\my_module\Plugin\LanguageNegotiation\MyLanguageNegotiationUrl';
|
||||
* }
|
||||
*
|
||||
* class MyLanguageNegotiationUrl extends LanguageNegotiationUrl {
|
||||
* public function getCurrentLanguage(Request $request = NULL) {
|
||||
* if ($request) {
|
||||
* // Use the original URL language negotiation method to get a valid
|
||||
* // language code.
|
||||
* $langcode = parent::getCurrentLanguage($request);
|
||||
*
|
||||
* // If we are on an administrative path, override with the default
|
||||
* language.
|
||||
* if ($request->query->has('q') && strtok($request->query->get('q'), '/') == 'admin') {
|
||||
* return $this->languageManager->getDefaultLanguage()->getId();
|
||||
* }
|
||||
* return $langcode;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* For more information, see
|
||||
* @link https://www.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 current active user and resets all language types.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $current_user
|
||||
* The current active user.
|
||||
*/
|
||||
public function setCurrentUser(AccountInterface $current_user);
|
||||
|
||||
/**
|
||||
* Initializes the specified language type.
|
||||
*
|
||||
* @param string $type
|
||||
* The language type to be initialized.
|
||||
*
|
||||
* @return \Drupal\Core\Language\LanguageInterface[]
|
||||
* Returns an array containing a single language keyed by the language
|
||||
* negotiation method ID used to determine the language of the specified
|
||||
* type. If negotiation is not possible the default language is returned.
|
||||
*/
|
||||
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 string $type
|
||||
* The language type.
|
||||
*
|
||||
* @return string
|
||||
* 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 string $method_id
|
||||
* The language negotiation method ID.
|
||||
* @param string $type
|
||||
* (optional) The language type. If none is passed, all the configurable
|
||||
* language types will be inspected.
|
||||
*
|
||||
* @return bool
|
||||
* 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 int[] $enabled_methods
|
||||
* An array of language negotiation method weights keyed by method ID.
|
||||
*/
|
||||
public function saveConfiguration($type, $enabled_methods);
|
||||
|
||||
/**
|
||||
* Resave the configuration to purge missing negotiation methods.
|
||||
*/
|
||||
public function purgeConfiguration();
|
||||
|
||||
/**
|
||||
* Updates the configuration based on the given language types.
|
||||
*
|
||||
* Stores the list of the language types along with information about their
|
||||
* configurable state. Stores the default settings if the language type is
|
||||
* not configurable.
|
||||
*
|
||||
* @param string[] $types
|
||||
* An array of configurable language types.
|
||||
*/
|
||||
public function updateConfiguration(array $types);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
use Drupal\Core\Config\BootstrapConfigStorageFactory;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\DependencyInjection\ServiceProviderBase;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Overrides the language_manager service to point to language's module one.
|
||||
*/
|
||||
class LanguageServiceProvider extends ServiceProviderBase {
|
||||
|
||||
const CONFIG_PREFIX = 'language.entity.';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function register(ContainerBuilder $container) {
|
||||
// The following services are needed only on multilingual sites.
|
||||
if ($this->isMultilingual()) {
|
||||
$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', ['priority' => 300])
|
||||
->addTag('path_processor_outbound', ['priority' => 100])
|
||||
->addArgument(new Reference('config.factory'))
|
||||
->addArgument(new Reference('language_manager'))
|
||||
->addArgument(new Reference('language_negotiator'))
|
||||
->addArgument(new Reference('current_user'))
|
||||
->addArgument(new Reference('language.config_subscriber'))
|
||||
->addMethodCall('initConfigSubscriber');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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'))
|
||||
->addArgument(new Reference('language.config_factory_override'))
|
||||
->addArgument(new Reference('request_stack'));
|
||||
if ($default_language_values = $this->getDefaultLanguageValues()) {
|
||||
$container->setParameter('language.default_values', $default_language_values);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the site is multilingual.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the site is multilingual, FALSE otherwise.
|
||||
*/
|
||||
protected function isMultilingual() {
|
||||
// Assign the prefix to a local variable so it can be used in an anonymous
|
||||
// function.
|
||||
$prefix = static::CONFIG_PREFIX;
|
||||
// @todo Try to swap out for config.storage to take advantage of database
|
||||
// and caching. This might prove difficult as this is called before the
|
||||
// container has finished building.
|
||||
$config_storage = BootstrapConfigStorageFactory::get();
|
||||
$config_ids = array_filter($config_storage->listAll($prefix), function ($config_id) use ($prefix) {
|
||||
return $config_id != $prefix . LanguageInterface::LANGCODE_NOT_SPECIFIED && $config_id != $prefix . LanguageInterface::LANGCODE_NOT_APPLICABLE;
|
||||
});
|
||||
return count($config_ids) > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default language values.
|
||||
*
|
||||
* @return array|bool
|
||||
* Returns the default language values for the language configured in
|
||||
* system.site:default_langcode if the corresponding configuration entity
|
||||
* exists, otherwise FALSE.
|
||||
*/
|
||||
protected function getDefaultLanguageValues() {
|
||||
$config_storage = BootstrapConfigStorageFactory::get();
|
||||
$system = $config_storage->read('system.site');
|
||||
// In Kernel tests it's possible this code is called before system.site
|
||||
// exists. In such cases behave as though the corresponding language
|
||||
// configuration entity does not exist.
|
||||
if ($system === FALSE) {
|
||||
return FALSE;
|
||||
}
|
||||
$default_language = $config_storage->read(static::CONFIG_PREFIX . $system['default_langcode']);
|
||||
if (is_array($default_language)) {
|
||||
return $default_language;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Interface for language switcher classes.
|
||||
*/
|
||||
interface LanguageSwitcherInterface {
|
||||
|
||||
/**
|
||||
* Returns language switch links.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
* @param string $type
|
||||
* The language type.
|
||||
* @param \Drupal\Core\Url $url
|
||||
* The URL the switch links will be relative to.
|
||||
*
|
||||
* @return array
|
||||
* An array of link arrays keyed by language code.
|
||||
*/
|
||||
public function getLanguageSwitchLinks(Request $request, $type, Url $url);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
use Drupal\Core\Path\PathMatcherInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a 'Language switcher' block.
|
||||
*
|
||||
* @Block(
|
||||
* id = "language_block",
|
||||
* admin_label = @Translation("Language switcher"),
|
||||
* category = @Translation("System"),
|
||||
* deriver = "Drupal\language\Plugin\Derivative\LanguageBlock"
|
||||
* )
|
||||
*/
|
||||
class LanguageBlock extends BlockBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The path matcher.
|
||||
*
|
||||
* @var \Drupal\Core\Path\PathMatcherInterface
|
||||
*/
|
||||
protected $pathMatcher;
|
||||
|
||||
/**
|
||||
* Constructs an LanguageBlock object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\Path\PathMatcherInterface $path_matcher
|
||||
* The path matcher.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, LanguageManagerInterface $language_manager, PathMatcherInterface $path_matcher) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->languageManager = $language_manager;
|
||||
$this->pathMatcher = $path_matcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('language_manager'),
|
||||
$container->get('path.matcher')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function blockAccess(AccountInterface $account) {
|
||||
$access = $this->languageManager->isMultilingual() ? AccessResult::allowed() : AccessResult::forbidden();
|
||||
return $access->addCacheTags(['config:configurable_language_list']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
$build = [];
|
||||
$route_name = $this->pathMatcher->isFrontPage() ? '<front>' : '<current>';
|
||||
$type = $this->getDerivativeId();
|
||||
$links = $this->languageManager->getLanguageSwitchLinks($type, Url::fromRoute($route_name));
|
||||
|
||||
if (isset($links->links)) {
|
||||
$build = [
|
||||
'#theme' => 'links__language_block',
|
||||
'#links' => $links->links,
|
||||
'#attributes' => [
|
||||
'class' => [
|
||||
"language-switcher-{$links->method_id}",
|
||||
],
|
||||
],
|
||||
'#set_active_class' => TRUE,
|
||||
];
|
||||
}
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @todo Make cacheable in https://www.drupal.org/node/2232375.
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\Condition;
|
||||
|
||||
use Drupal\Core\Condition\ConditionPluginBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a 'Language' condition.
|
||||
*
|
||||
* @Condition(
|
||||
* id = "language",
|
||||
* label = @Translation("Language"),
|
||||
* context_definitions = {
|
||||
* "language" = @ContextDefinition("language", label = @Translation("Language"))
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class Language extends ConditionPluginBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The Language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* Creates a new Language instance.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param array $configuration
|
||||
* The plugin configuration, i.e. an array with configuration values keyed
|
||||
* by configuration option name. The special key 'context' may be used to
|
||||
* initialize the defined contexts by setting it to an array of context
|
||||
* values keyed by context names.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
*/
|
||||
public function __construct(LanguageManagerInterface $language_manager, array $configuration, $plugin_id, $plugin_definition) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$container->get('language_manager'),
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
if ($this->languageManager->isMultilingual()) {
|
||||
// Fetch languages.
|
||||
$languages = $this->languageManager->getLanguages();
|
||||
$langcodes_options = [];
|
||||
foreach ($languages as $language) {
|
||||
$langcodes_options[$language->getId()] = $language->getName();
|
||||
}
|
||||
$form['langcodes'] = [
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('Language selection'),
|
||||
'#default_value' => $this->configuration['langcodes'],
|
||||
'#options' => $langcodes_options,
|
||||
'#description' => $this->t('Select languages to enforce. If none are selected, all languages will be allowed.'),
|
||||
];
|
||||
}
|
||||
else {
|
||||
$form['langcodes'] = [
|
||||
'#type' => 'value',
|
||||
'#default_value' => $this->configuration['langcodes'],
|
||||
];
|
||||
}
|
||||
return parent::buildConfigurationForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->configuration['langcodes'] = array_filter($form_state->getValue('langcodes'));
|
||||
parent::submitConfigurationForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function summary() {
|
||||
$language_list = $this->languageManager->getLanguages(LanguageInterface::STATE_ALL);
|
||||
$selected = $this->configuration['langcodes'];
|
||||
// Reduce the language list to an array of language names.
|
||||
$language_names = array_reduce($language_list, function (&$result, $item) use ($selected) {
|
||||
// If the current item of the $language_list array is one of the selected
|
||||
// languages, add it to the $results array.
|
||||
if (!empty($selected[$item->getId()])) {
|
||||
$result[$item->getId()] = $item->getName();
|
||||
}
|
||||
return $result;
|
||||
}, []);
|
||||
|
||||
// If we have more than one language selected, separate them by commas.
|
||||
if (count($this->configuration['langcodes']) > 1) {
|
||||
$languages = implode(', ', $language_names);
|
||||
}
|
||||
else {
|
||||
// If we have just one language just grab the only present value.
|
||||
$languages = array_pop($language_names);
|
||||
}
|
||||
if (!empty($this->configuration['negate'])) {
|
||||
return t('The language is not @languages.', ['@languages' => $languages]);
|
||||
}
|
||||
return t('The language is @languages.', ['@languages' => $languages]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function evaluate() {
|
||||
if (empty($this->configuration['langcodes']) && !$this->isNegated()) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
$language = $this->getContextValue('language');
|
||||
// Language visibility settings.
|
||||
return !empty($this->configuration['langcodes'][$language->getId()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return ['langcodes' => []] + parent::defaultConfiguration();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\language\ConfigurableLanguageManagerInterface;
|
||||
|
||||
/**
|
||||
* Provides language switcher block plugin definitions for all languages.
|
||||
*/
|
||||
class LanguageBlock extends DeriverBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
$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)', ['@type' => $info[$type]['name']]);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\LanguageNegotiation;
|
||||
|
||||
use Drupal\Component\Utility\UserAgent;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\language\LanguageNegotiationMethodBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Class for identifying language from the browser Accept-language HTTP header.
|
||||
*
|
||||
* @LanguageNegotiation(
|
||||
* id = \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser::METHOD_ID,
|
||||
* weight = -2,
|
||||
* name = @Translation("Browser"),
|
||||
* description = @Translation("Language from the browser's language settings."),
|
||||
* config_route_name = "language.negotiation_browser"
|
||||
* )
|
||||
*/
|
||||
class LanguageNegotiationBrowser extends LanguageNegotiationMethodBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The language negotiation method id.
|
||||
*/
|
||||
const METHOD_ID = 'language-browser';
|
||||
|
||||
/**
|
||||
* The page cache disabling policy.
|
||||
*
|
||||
* @var \Drupal\Core\PageCache\ResponsePolicy\KillSwitch
|
||||
*/
|
||||
protected $pageCacheKillSwitch;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
$instance = new static();
|
||||
$instance->pageCacheKillSwitch = $container->get('page_cache_kill_switch');
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLangcode(Request $request = NULL) {
|
||||
$langcode = NULL;
|
||||
|
||||
if ($this->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('map');
|
||||
$langcode = UserAgent::getBestMatchingLangcode($http_accept_language, $langcodes, $mappings);
|
||||
}
|
||||
|
||||
// Internal page cache with multiple languages and browser negotiation
|
||||
// could lead to wrong cached sites. Therefore disabling the internal page
|
||||
// cache.
|
||||
// @todo Solve more elegantly in https://www.drupal.org/node/2430335.
|
||||
$this->pageCacheKillSwitch->trigger();
|
||||
|
||||
return $langcode;
|
||||
}
|
||||
|
||||
}
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\LanguageNegotiation;
|
||||
|
||||
use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\language\LanguageNegotiationMethodBase;
|
||||
use Drupal\language\LanguageSwitcherInterface;
|
||||
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Class for identifying the content translation language.
|
||||
*
|
||||
* @LanguageNegotiation(
|
||||
* id = Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity::METHOD_ID,
|
||||
* types = {Drupal\Core\Language\LanguageInterface::TYPE_CONTENT},
|
||||
* weight = -9,
|
||||
* name = @Translation("Content language"),
|
||||
* description = @Translation("Determines the content language from a request parameter."),
|
||||
* )
|
||||
*/
|
||||
class LanguageNegotiationContentEntity extends LanguageNegotiationMethodBase implements OutboundPathProcessorInterface, LanguageSwitcherInterface, ContainerFactoryPluginInterface {
|
||||
use DeprecatedServicePropertyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $deprecatedProperties = ['entityManager' => 'entity.manager'];
|
||||
|
||||
/**
|
||||
* The language negotiation method ID.
|
||||
*/
|
||||
const METHOD_ID = 'language-content-entity';
|
||||
|
||||
/**
|
||||
* The query string parameter.
|
||||
*/
|
||||
const QUERY_PARAMETER = 'language_content_entity';
|
||||
|
||||
/**
|
||||
* A list of all the link paths of enabled content entities.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $contentEntityPaths;
|
||||
|
||||
/**
|
||||
* Static cache for the language negotiation order check.
|
||||
*
|
||||
* @see \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity::hasLowerLanguageNegotiationWeight()
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $hasLowerLanguageNegotiationWeightResult;
|
||||
|
||||
/**
|
||||
* Static cache of outbound route paths per request.
|
||||
*
|
||||
* @var \SplObjectStorage
|
||||
*/
|
||||
protected $paths;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a new LanguageNegotiationContentEntity instance.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->paths = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static($container->get('entity_type.manager'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLangcode(Request $request = NULL) {
|
||||
$langcode = $request->query->get(static::QUERY_PARAMETER);
|
||||
|
||||
$language_enabled = array_key_exists($langcode, $this->languageManager->getLanguages());
|
||||
return $language_enabled ? $langcode : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
|
||||
// If appropriate, process outbound to add a query parameter to the url and
|
||||
// remove the language option, so that url negotiator does not rewrite the
|
||||
// url.
|
||||
|
||||
// First, check if processing conditions are met.
|
||||
if (!($request && !empty($options['route']) && $this->hasLowerLanguageNegotiationWeight() && $this->meetsContentEntityRoutesCondition($options['route'], $request))) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
if (isset($options['language']) || $langcode = $this->getLangcode($request)) {
|
||||
// If the language option is set, unset it, so that the url language
|
||||
// negotiator does not rewrite the url.
|
||||
if (isset($options['language'])) {
|
||||
$langcode = $options['language']->getId();
|
||||
unset($options['language']);
|
||||
}
|
||||
|
||||
if (!isset($options['query'][static::QUERY_PARAMETER])) {
|
||||
$options['query'][static::QUERY_PARAMETER] = $langcode;
|
||||
}
|
||||
|
||||
if ($bubbleable_metadata) {
|
||||
// Cached URLs that have been processed by this outbound path
|
||||
// processor must be:
|
||||
$bubbleable_metadata
|
||||
// - varied by the content language query parameter.
|
||||
->addCacheContexts(['url.query_args:' . static::QUERY_PARAMETER]);
|
||||
}
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLanguageSwitchLinks(Request $request, $type, Url $url) {
|
||||
$links = [];
|
||||
$query = [];
|
||||
parse_str($request->getQueryString(), $query);
|
||||
|
||||
foreach ($this->languageManager->getNativeLanguages() as $language) {
|
||||
$langcode = $language->getId();
|
||||
$query[static::QUERY_PARAMETER] = $langcode;
|
||||
$links[$langcode] = [
|
||||
'url' => $url,
|
||||
'title' => $language->getName(),
|
||||
'attributes' => ['class' => ['language-link']],
|
||||
'query' => $query,
|
||||
];
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if content entity language negotiator has higher priority.
|
||||
*
|
||||
* The content entity language negotiator having higher priority than the url
|
||||
* language negotiator, is a criteria in
|
||||
* \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity::processOutbound().
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the content entity language negotiator has higher priority than
|
||||
* the url language negotiator, FALSE otherwise.
|
||||
*/
|
||||
protected function hasLowerLanguageNegotiationWeight() {
|
||||
if (!isset($this->hasLowerLanguageNegotiationWeightResult)) {
|
||||
// Only run if the LanguageNegotiationContentEntity outbound function is
|
||||
// being executed before the outbound function of LanguageNegotiationUrl.
|
||||
$content_method_weights = $this->config->get('language.types')->get('negotiation.language_content.enabled') ?: [];
|
||||
|
||||
// Check if the content language is configured to be dependent on the
|
||||
// url negotiator directly or indirectly over the interface negotiator.
|
||||
if (isset($content_method_weights[LanguageNegotiationUrl::METHOD_ID]) && ($content_method_weights[static::METHOD_ID] > $content_method_weights[LanguageNegotiationUrl::METHOD_ID])) {
|
||||
$this->hasLowerLanguageNegotiationWeightResult = FALSE;
|
||||
}
|
||||
else {
|
||||
$check_interface_method = FALSE;
|
||||
if (isset($content_method_weights[LanguageNegotiationUI::METHOD_ID])) {
|
||||
$interface_method_weights = $this->config->get('language.types')->get('negotiation.language_interface.enabled') ?: [];
|
||||
$check_interface_method = isset($interface_method_weights[LanguageNegotiationUrl::METHOD_ID]);
|
||||
}
|
||||
if ($check_interface_method) {
|
||||
$max_weight = $content_method_weights[LanguageNegotiationUI::METHOD_ID];
|
||||
$max_weight = isset($content_method_weights[LanguageNegotiationUrl::METHOD_ID]) ? max($max_weight, $content_method_weights[LanguageNegotiationUrl::METHOD_ID]) : $max_weight;
|
||||
}
|
||||
else {
|
||||
$max_weight = isset($content_method_weights[LanguageNegotiationUrl::METHOD_ID]) ? $content_method_weights[LanguageNegotiationUrl::METHOD_ID] : PHP_INT_MAX;
|
||||
}
|
||||
|
||||
$this->hasLowerLanguageNegotiationWeightResult = $content_method_weights[static::METHOD_ID] < $max_weight;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->hasLowerLanguageNegotiationWeightResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if content entity route condition is met.
|
||||
*
|
||||
* Requirements: currently being on an content entity route and processing
|
||||
* outbound url pointing to the same content entity.
|
||||
*
|
||||
* @param \Symfony\Component\Routing\Route $outbound_route
|
||||
* The route object for the current outbound url being processed.
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The HttpRequest object representing the current request.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the content entity route condition is met, FALSE otherwise.
|
||||
*/
|
||||
protected function meetsContentEntityRoutesCondition(Route $outbound_route, Request $request) {
|
||||
$outbound_path_pattern = $outbound_route->getPath();
|
||||
$storage = isset($this->paths[$request]) ? $this->paths[$request] : [];
|
||||
if (!isset($storage[$outbound_path_pattern])) {
|
||||
$storage[$outbound_path_pattern] = FALSE;
|
||||
|
||||
// Check if the outbound route points to the current entity.
|
||||
if ($content_entity_type_id_for_current_route = $this->getContentEntityTypeIdForCurrentRequest($request)) {
|
||||
if (!empty($this->getContentEntityPaths()[$outbound_path_pattern]) && $content_entity_type_id_for_current_route == $this->getContentEntityPaths()[$outbound_path_pattern]) {
|
||||
$storage[$outbound_path_pattern] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
$this->paths[$request] = $storage;
|
||||
}
|
||||
|
||||
return $storage[$outbound_path_pattern];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content entity type ID from the current request for the route.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The HttpRequest object representing the current request.
|
||||
*
|
||||
* @return string
|
||||
* The entity type ID for the route from the request.
|
||||
*/
|
||||
protected function getContentEntityTypeIdForCurrentRequest(Request $request) {
|
||||
$content_entity_type_id_for_current_route = '';
|
||||
|
||||
if ($current_route = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)) {
|
||||
$current_route_path = $current_route->getPath();
|
||||
$content_entity_type_id_for_current_route = isset($this->getContentEntityPaths()[$current_route_path]) ? $this->getContentEntityPaths()[$current_route_path] : '';
|
||||
}
|
||||
|
||||
return $content_entity_type_id_for_current_route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths for the link templates of all content entities.
|
||||
*
|
||||
* @return array
|
||||
* An array of all content entity type IDs, keyed by the corresponding link
|
||||
* template paths.
|
||||
*/
|
||||
protected function getContentEntityPaths() {
|
||||
if (!isset($this->contentEntityPaths)) {
|
||||
$this->contentEntityPaths = [];
|
||||
$entity_types = $this->entityTypeManager->getDefinitions();
|
||||
foreach ($entity_types as $entity_type_id => $entity_type) {
|
||||
if ($entity_type->entityClassImplements(ContentEntityInterface::class)) {
|
||||
$entity_paths = array_fill_keys($entity_type->getLinkTemplates(), $entity_type_id);
|
||||
$this->contentEntityPaths = array_merge($this->contentEntityPaths, $entity_paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->contentEntityPaths;
|
||||
}
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\LanguageNegotiation;
|
||||
|
||||
use Drupal\language\LanguageNegotiationMethodBase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Class for identifying language from a selected language.
|
||||
*
|
||||
* @LanguageNegotiation(
|
||||
* id = Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSelected::METHOD_ID,
|
||||
* weight = 12,
|
||||
* name = @Translation("Selected language"),
|
||||
* description = @Translation("Language based on a selected language."),
|
||||
* config_route_name = "language.negotiation_selected"
|
||||
* )
|
||||
*/
|
||||
class LanguageNegotiationSelected extends LanguageNegotiationMethodBase {
|
||||
|
||||
/**
|
||||
* The language negotiation method id.
|
||||
*/
|
||||
const METHOD_ID = 'language-selected';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLangcode(Request $request = NULL) {
|
||||
$langcode = NULL;
|
||||
|
||||
if ($this->languageManager) {
|
||||
$langcode = $this->config->get('language.negotiation')->get('selected_langcode');
|
||||
}
|
||||
|
||||
return $langcode;
|
||||
}
|
||||
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\LanguageNegotiation;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\language\LanguageNegotiationMethodBase;
|
||||
use Drupal\language\LanguageSwitcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Identify language from a request/session parameter.
|
||||
*
|
||||
* @LanguageNegotiation(
|
||||
* id = Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSession::METHOD_ID,
|
||||
* weight = -6,
|
||||
* name = @Translation("Session"),
|
||||
* description = @Translation("Language from a request/session parameter."),
|
||||
* config_route_name = "language.negotiation_session"
|
||||
* )
|
||||
*/
|
||||
class LanguageNegotiationSession extends LanguageNegotiationMethodBase implements OutboundPathProcessorInterface, LanguageSwitcherInterface {
|
||||
|
||||
/**
|
||||
* Flag used to determine whether query rewriting is active.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $queryRewrite;
|
||||
|
||||
/**
|
||||
* The query parameter name to rewrite.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $queryParam;
|
||||
|
||||
/**
|
||||
* The query parameter value to be set.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $queryValue;
|
||||
|
||||
/**
|
||||
* The language negotiation method id.
|
||||
*/
|
||||
const METHOD_ID = 'language-session';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLangcode(Request $request = NULL) {
|
||||
$config = $this->config->get('language.negotiation')->get('session');
|
||||
$param = $config['parameter'];
|
||||
$langcode = $request && $request->query->get($param) ? $request->query->get($param) : NULL;
|
||||
if (!$langcode && isset($_SESSION[$param])) {
|
||||
$langcode = $_SESSION[$param];
|
||||
}
|
||||
return $langcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function persist(LanguageInterface $language) {
|
||||
// We need to update the session parameter with the request value only if we
|
||||
// have an authenticated user.
|
||||
$langcode = $language->getId();
|
||||
if ($langcode && $this->languageManager) {
|
||||
$languages = $this->languageManager->getLanguages();
|
||||
if ($this->currentUser->isAuthenticated() && isset($languages[$langcode])) {
|
||||
$config = $this->config->get('language.negotiation')->get('session');
|
||||
$_SESSION[$config['parameter']] = $langcode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = 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'][$this->queryParam])) {
|
||||
$options['query'][$this->queryParam] = $this->queryValue;
|
||||
}
|
||||
if ($bubbleable_metadata) {
|
||||
// Cached URLs that have been processed by this outbound path
|
||||
// processor must be:
|
||||
$bubbleable_metadata
|
||||
// - invalidated when the language negotiation config changes, since
|
||||
// another query parameter may be used to determine the language.
|
||||
->addCacheTags($this->config->get('language.negotiation')->getCacheTags())
|
||||
// - varied by the configured query parameter.
|
||||
->addCacheContexts(['url.query_args:' . $this->queryParam]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLanguageSwitchLinks(Request $request, $type, Url $url) {
|
||||
$links = [];
|
||||
$config = $this->config->get('language.negotiation')->get('session');
|
||||
$param = $config['parameter'];
|
||||
$language_query = isset($_SESSION[$param]) ? $_SESSION[$param] : $this->languageManager->getCurrentLanguage($type)->getId();
|
||||
$query = [];
|
||||
parse_str($request->getQueryString(), $query);
|
||||
|
||||
foreach ($this->languageManager->getNativeLanguages() as $language) {
|
||||
$langcode = $language->getId();
|
||||
$links[$langcode] = [
|
||||
// We need to clone the $url object to avoid using the same one for all
|
||||
// links. When the links are rendered, options are set on the $url
|
||||
// object, so if we use the same one, they would be set for all links.
|
||||
'url' => clone $url,
|
||||
'title' => $language->getName(),
|
||||
'attributes' => ['class' => ['language-link']],
|
||||
'query' => $query,
|
||||
];
|
||||
if ($language_query != $langcode) {
|
||||
$links[$langcode]['query'][$param] = $langcode;
|
||||
}
|
||||
else {
|
||||
$links[$langcode]['attributes']['class'][] = 'session-active';
|
||||
}
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\LanguageNegotiation;
|
||||
|
||||
use Drupal\language\LanguageNegotiationMethodBase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Identifies the language from the interface text language selected for page.
|
||||
*
|
||||
* @LanguageNegotiation(
|
||||
* id = Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUI::METHOD_ID,
|
||||
* types = {Drupal\Core\Language\LanguageInterface::TYPE_CONTENT},
|
||||
* weight = 9,
|
||||
* name = @Translation("Interface"),
|
||||
* description = @Translation("Use the detected interface language.")
|
||||
* )
|
||||
*/
|
||||
class LanguageNegotiationUI extends LanguageNegotiationMethodBase {
|
||||
|
||||
/**
|
||||
* The language negotiation method id.
|
||||
*/
|
||||
const METHOD_ID = 'language-interface';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLangcode(Request $request = NULL) {
|
||||
return $this->languageManager ? $this->languageManager->getCurrentLanguage()->getId() : NULL;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\LanguageNegotiation;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
|
||||
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\language\LanguageNegotiationMethodBase;
|
||||
use Drupal\language\LanguageSwitcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Class for identifying language via URL prefix or domain.
|
||||
*
|
||||
* @LanguageNegotiation(
|
||||
* id = \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl::METHOD_ID,
|
||||
* types = {\Drupal\Core\Language\LanguageInterface::TYPE_INTERFACE,
|
||||
* \Drupal\Core\Language\LanguageInterface::TYPE_CONTENT,
|
||||
* \Drupal\Core\Language\LanguageInterface::TYPE_URL},
|
||||
* weight = -8,
|
||||
* name = @Translation("URL"),
|
||||
* description = @Translation("Language from the URL (Path prefix or domain)."),
|
||||
* config_route_name = "language.negotiation_url"
|
||||
* )
|
||||
*/
|
||||
class LanguageNegotiationUrl extends LanguageNegotiationMethodBase implements InboundPathProcessorInterface, OutboundPathProcessorInterface, LanguageSwitcherInterface {
|
||||
|
||||
/**
|
||||
* The language negotiation method id.
|
||||
*/
|
||||
const METHOD_ID = 'language-url';
|
||||
|
||||
/**
|
||||
* URL language negotiation: use the path prefix as URL language indicator.
|
||||
*/
|
||||
const CONFIG_PATH_PREFIX = 'path_prefix';
|
||||
|
||||
/**
|
||||
* URL language negotiation: use the domain as URL language indicator.
|
||||
*/
|
||||
const CONFIG_DOMAIN = 'domain';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLangcode(Request $request = NULL) {
|
||||
$langcode = NULL;
|
||||
|
||||
if ($request && $this->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 added languages.
|
||||
$negotiated_language = FALSE;
|
||||
foreach ($languages as $language) {
|
||||
if (isset($config['prefixes'][$language->getId()]) && $config['prefixes'][$language->getId()] == $prefix) {
|
||||
$negotiated_language = $language;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($negotiated_language) {
|
||||
$langcode = $negotiated_language->getId();
|
||||
}
|
||||
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->getId()])) {
|
||||
// Ensure that there is exactly one protocol in the URL when
|
||||
// checking the hostname.
|
||||
$host = 'http://' . str_replace(['http://', 'https://'], '', $config['domains'][$language->getId()]);
|
||||
$host = parse_url($host, PHP_URL_HOST);
|
||||
if ($http_host == $host) {
|
||||
$langcode = $language->getId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $langcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processInbound($path, Request $request) {
|
||||
$config = $this->config->get('language.negotiation')->get('url');
|
||||
|
||||
if ($config['source'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX) {
|
||||
$parts = explode('/', trim($path, '/'));
|
||||
$prefix = array_shift($parts);
|
||||
|
||||
// Search prefix within added languages.
|
||||
foreach ($this->languageManager->getLanguages() as $language) {
|
||||
if (isset($config['prefixes'][$language->getId()]) && $config['prefixes'][$language->getId()] == $prefix) {
|
||||
// Rebuild $path with the language removed.
|
||||
$path = '/' . implode('/', $parts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = 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(LanguageInterface::TYPE_URL);
|
||||
$options['language'] = $language_url;
|
||||
}
|
||||
// We allow only added languages here.
|
||||
elseif (!is_object($options['language']) || !isset($languages[$options['language']->getId()])) {
|
||||
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']->getId()])) {
|
||||
$options['prefix'] = $config['prefixes'][$options['language']->getId()] . '/';
|
||||
if ($bubbleable_metadata) {
|
||||
$bubbleable_metadata->addCacheContexts(['languages:' . LanguageInterface::TYPE_URL]);
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($config['source'] == LanguageNegotiationUrl::CONFIG_DOMAIN) {
|
||||
if (is_object($options['language']) && !empty($config['domains'][$options['language']->getId()])) {
|
||||
|
||||
// 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(['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']->getId()];
|
||||
|
||||
// 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 (($url_scheme == 'http' && $port != 80) || ($url_scheme == 'https' && $port != 443)) {
|
||||
$options['base_url'] .= ':' . $port;
|
||||
}
|
||||
|
||||
if (isset($options['https'])) {
|
||||
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(), '/');
|
||||
if ($bubbleable_metadata) {
|
||||
$bubbleable_metadata->addCacheContexts(['languages:' . LanguageInterface::TYPE_URL, 'url.site']);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLanguageSwitchLinks(Request $request, $type, Url $url) {
|
||||
$links = [];
|
||||
$query = $request->query->all();
|
||||
|
||||
foreach ($this->languageManager->getNativeLanguages() as $language) {
|
||||
$links[$language->getId()] = [
|
||||
// We need to clone the $url object to avoid using the same one for all
|
||||
// links. When the links are rendered, options are set on the $url
|
||||
// object, so if we use the same one, they would be set for all links.
|
||||
'url' => clone $url,
|
||||
'title' => $language->getName(),
|
||||
'language' => $language,
|
||||
'attributes' => ['class' => ['language-link']],
|
||||
'query' => $query,
|
||||
];
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\LanguageNegotiation;
|
||||
|
||||
use Drupal\language\LanguageNegotiationMethodBase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Class that 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.
|
||||
*
|
||||
* @LanguageNegotiation(
|
||||
* id = Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrlFallback::METHOD_ID,
|
||||
* types = {Drupal\Core\Language\LanguageInterface::TYPE_URL},
|
||||
* weight = 8,
|
||||
* name = @Translation("URL fallback"),
|
||||
* description = @Translation("Use an already detected language for URLs if none is found.")
|
||||
* )
|
||||
*/
|
||||
class LanguageNegotiationUrlFallback extends LanguageNegotiationMethodBase {
|
||||
|
||||
/**
|
||||
* The language negotiation method id.
|
||||
*/
|
||||
const METHOD_ID = 'language-url-fallback';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLangcode(Request $request = NULL) {
|
||||
$langcode = NULL;
|
||||
|
||||
if ($this->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->getId()])) || (!$prefix && empty($config['domains'][$default->getId()]))) {
|
||||
$langcode = $default->getId();
|
||||
}
|
||||
else {
|
||||
$langcode = $this->languageManager->getCurrentLanguage()->getId();
|
||||
}
|
||||
}
|
||||
|
||||
return $langcode;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\migrate\destination;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\Plugin\migrate\destination\Config;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Provides a destination plugin for the default langcode config.
|
||||
*
|
||||
* @MigrateDestination(
|
||||
* id = "default_langcode"
|
||||
* )
|
||||
*/
|
||||
class DefaultLangcode extends Config {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function import(Row $row, array $old_destination_id_values = []) {
|
||||
$destination = $row->getDestination();
|
||||
$langcode = $destination['default_langcode'];
|
||||
|
||||
// Check if the language exists.
|
||||
if (ConfigurableLanguage::load($langcode) === NULL) {
|
||||
throw new MigrateException("The language '$langcode' does not exist on this site.");
|
||||
}
|
||||
|
||||
$this->config->set('default_langcode', $destination['default_langcode']);
|
||||
$this->config->save();
|
||||
return [$this->config->getName()];
|
||||
}
|
||||
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\migrate\process;
|
||||
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Determines the content translation setting.
|
||||
*
|
||||
* The source value is an indexed array of three values:
|
||||
* - The language content type, e.g. '1'
|
||||
* - The entity_translation_entity_types, an array of entity types.
|
||||
* - An entity type used with entity translation, e.g. comment.
|
||||
*
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "content_translation_enabled_setting"
|
||||
* )
|
||||
*/
|
||||
class ContentTranslationEnabledSetting extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
if (!is_array($value)) {
|
||||
throw new MigrateException('Input should be an array');
|
||||
}
|
||||
|
||||
list($language_content_type, $entity_translation_entity_types, $entity_type) = $value;
|
||||
|
||||
switch ($language_content_type) {
|
||||
// In the case of being 0, it will be skipped. We are not actually setting
|
||||
// a null value.
|
||||
case 0;
|
||||
$setting = NULL;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
$setting = FALSE;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$setting = FALSE;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// If entity translation is enabled return the status of comment
|
||||
// translations.
|
||||
$setting = FALSE;
|
||||
if (!empty($entity_translation_entity_types[$entity_type])) {
|
||||
$setting = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$setting = NULL;
|
||||
break;
|
||||
}
|
||||
return $setting;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\migrate\process;
|
||||
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\Plugin\migrate\process\ArrayBuild;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* This plugin makes sure that no domain is empty if domain negotiation is used.
|
||||
*
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "language_domains",
|
||||
* handle_multiples = TRUE
|
||||
* )
|
||||
*/
|
||||
class LanguageDomains extends ArrayBuild {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
if ($row->getSourceProperty('domain_negotiation_used')) {
|
||||
global $base_url;
|
||||
|
||||
foreach ($value as $old_key => $old_value) {
|
||||
if (empty($old_value['domain'])) {
|
||||
// The default language domain might be empty.
|
||||
// If it is, use the current domain.
|
||||
$value[$old_key]['domain'] = parse_url($base_url, PHP_URL_HOST);
|
||||
}
|
||||
else {
|
||||
// Ensure we have a protocol when checking for the hostname.
|
||||
$domain = 'http://' . str_replace(['http://', 'https://'], '', $old_value['domain']);
|
||||
// Only keep the host part of the domain.
|
||||
$value[$old_key]['domain'] = parse_url($domain, PHP_URL_HOST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent::transform($value, $migrate_executable, $row, $destination_property);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\migrate\process;
|
||||
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Processes the arrays for the language types' negotiation methods and weights.
|
||||
*
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "language_negotiation",
|
||||
* handle_multiples = TRUE
|
||||
* )
|
||||
*/
|
||||
class LanguageNegotiation extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
$new_value = [
|
||||
'enabled' => [],
|
||||
'method_weights' => [],
|
||||
];
|
||||
|
||||
if (!is_array($value)) {
|
||||
throw new MigrateException('The input should be an array');
|
||||
}
|
||||
|
||||
// If no weights are provided, use the keys by flipping the array.
|
||||
if (empty($value[1])) {
|
||||
$new_value['enabled'] = array_flip(array_map([$this, 'mapNewMethods'], array_keys($value[0])));
|
||||
unset($new_value['method_weights']);
|
||||
}
|
||||
else {
|
||||
foreach ($value[1] as $method => $weight) {
|
||||
$new_method = $this->mapNewMethods($method);
|
||||
$new_value['method_weights'][$new_method] = $weight;
|
||||
if (in_array($method, array_keys($value[0]))) {
|
||||
$new_value['enabled'][$new_method] = $weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $new_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps old negotiation method names to the new ones.
|
||||
*
|
||||
* @param string $value
|
||||
* The old negotiation method name.
|
||||
*
|
||||
* @return string
|
||||
* The new negotiation method name.
|
||||
*/
|
||||
protected function mapNewMethods($value) {
|
||||
switch ($value) {
|
||||
case 'language-default':
|
||||
return 'language-selected';
|
||||
|
||||
case 'locale-browser':
|
||||
return 'language-browser';
|
||||
|
||||
case 'locale-interface':
|
||||
return 'language-interface';
|
||||
|
||||
case 'locale-session':
|
||||
return 'language-session';
|
||||
|
||||
case 'locale-url':
|
||||
return 'language-url';
|
||||
|
||||
case 'locale-url-fallback':
|
||||
return 'language-url-fallback';
|
||||
|
||||
case 'locale-user':
|
||||
return 'language-user';
|
||||
|
||||
default:
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\migrate\process;
|
||||
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Processes the array for the language types.
|
||||
*
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "language_types",
|
||||
* handle_multiples = TRUE
|
||||
* )
|
||||
*/
|
||||
class LanguageTypes extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
if (!is_array($value)) {
|
||||
throw new MigrateException('The input should be an array');
|
||||
}
|
||||
|
||||
if (array_key_exists('language', $value)) {
|
||||
$value['language_interface'] = $value['language'];
|
||||
unset($value['language']);
|
||||
}
|
||||
|
||||
if (!empty($this->configuration['filter_configurable'])) {
|
||||
$value = array_filter($value);
|
||||
}
|
||||
|
||||
return array_keys($value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* @MigrateSource(
|
||||
* id = "language",
|
||||
* source_module = "locale"
|
||||
* )
|
||||
*/
|
||||
class Language extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return [
|
||||
'language' => $this->t('The language code.'),
|
||||
'name' => $this->t('The English name of the language.'),
|
||||
'native' => $this->t('The native name of the language.'),
|
||||
'direction' => $this->t('The language direction. (0 = LTR, 1 = RTL)'),
|
||||
'enabled' => $this->t('Whether the language is enabled.'),
|
||||
'plurals' => $this->t('Number of plural indexes in this language.'),
|
||||
'formula' => $this->t('PHP formula to get plural indexes.'),
|
||||
'domain' => $this->t('Domain to use for this language.'),
|
||||
'prefix' => $this->t('Path prefix used for this language.'),
|
||||
'weight' => $this->t('The language weight when listed.'),
|
||||
'javascript' => $this->t('Location of the JavaScript translation file.'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return [
|
||||
'language' => [
|
||||
'type' => 'string',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
return $this->select('languages')->fields('languages');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row) {
|
||||
if (!empty($this->configuration['fetch_all'])) {
|
||||
// Get an array of all languages.
|
||||
$languages = $this->query()->execute()->fetchAll();
|
||||
$row->setSourceProperty('languages', $languages);
|
||||
}
|
||||
|
||||
if (!empty($this->configuration['domain_negotiation'])) {
|
||||
// Check if domain negotiation is used to be able to fill in the default
|
||||
// language domain, which may be empty. In D6, domain negotiation is used
|
||||
// when the 'language_negotiation' variable is set to '3', and in D7, when
|
||||
// the 'locale_language_negotiation_url_part' variable is set to '1'.
|
||||
if ($this->variableGet('language_negotiation', 0) == 3 || $this->variableGet('locale_language_negotiation_url_part', 0) == 1) {
|
||||
$row->setSourceProperty('domain_negotiation_used', TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
return parent::prepareRow($row);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\migrate\source\d6;
|
||||
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* Drupal multilingual node settings from database.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d6_language_content_settings",
|
||||
* source_module = "locale"
|
||||
* )
|
||||
*/
|
||||
class LanguageContentSettings extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
return $this->select('node_type', 't')
|
||||
->fields('t', [
|
||||
'type',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
$fields = [
|
||||
'type' => $this->t('Type'),
|
||||
'language_content_type' => $this->t('Multilingual support.'),
|
||||
'i18n_lock_node' => $this->t('Lock language.'),
|
||||
];
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row) {
|
||||
$type = $row->getSourceProperty('type');
|
||||
$row->setSourceProperty('language_content_type', $this->variableGet('language_content_type_' . $type, NULL));
|
||||
$row->setSourceProperty('i18n_lock_node', $this->variableGet('i18n_lock_node_' . $type, 0));
|
||||
return parent::prepareRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['type']['type'] = 'string';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\migrate\source\d6;
|
||||
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* Drupal 6 i18n vocabularies source from database.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d6_language_content_settings_taxonomy_vocabulary",
|
||||
* source_module = "taxonomy"
|
||||
* )
|
||||
*/
|
||||
class LanguageContentSettingsTaxonomyVocabulary extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
return $this->select('vocabulary', 'v')
|
||||
->fields('v', ['vid', 'language']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return [
|
||||
'vid' => $this->t('The vocabulary ID.'),
|
||||
'language' => $this->t('The default language for new terms.'),
|
||||
'state' => $this->t('The i18n taxonomy translation setting.'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row) {
|
||||
// Get the i18n taxonomy translation setting for this vocabulary.
|
||||
// 0 - No multilingual options
|
||||
// 1 - Localizable terms. Run through the localization system.
|
||||
// 2 - Predefined language for a vocabulary and its terms.
|
||||
// 3 - Per-language terms, translatable (referencing terms with different
|
||||
// languages) but not localizable.
|
||||
$i18ntaxonomy_vocabulary = $this->variableGet('i18ntaxonomy_vocabulary', NULL);
|
||||
$vid = $row->getSourceProperty('vid');
|
||||
$state = FALSE;
|
||||
if (array_key_exists($vid, $i18ntaxonomy_vocabulary)) {
|
||||
$state = $i18ntaxonomy_vocabulary[$vid];
|
||||
}
|
||||
$row->setSourceProperty('state', $state);
|
||||
return parent::prepareRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['vid']['type'] = 'integer';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\migrate\source\d7;
|
||||
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* Drupal multilingual node settings from database.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d7_language_content_settings",
|
||||
* source_module = "locale"
|
||||
* )
|
||||
*/
|
||||
class LanguageContentSettings extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
return $this->select('node_type', 't')
|
||||
->fields('t', [
|
||||
'type',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
$fields = [
|
||||
'type' => $this->t('Type'),
|
||||
'language_content_type' => $this->t('Multilingual support.'),
|
||||
'i18n_lock_node' => $this->t('Lock language.'),
|
||||
];
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row) {
|
||||
$type = $row->getSourceProperty('type');
|
||||
$row->setSourceProperty('language_content_type', $this->variableGet('language_content_type_' . $type, NULL));
|
||||
$i18n_node_options = $this->variableGet('i18n_node_options_' . $type, NULL);
|
||||
if ($i18n_node_options && in_array('lock', $i18n_node_options)) {
|
||||
$row->setSourceProperty('i18n_lock_node', 1);
|
||||
}
|
||||
else {
|
||||
$row->setSourceProperty('i18n_lock_node', 0);
|
||||
}
|
||||
|
||||
// Get the entity translation entity settings.
|
||||
$entity_translation_entity_types = $this->variableGet('entity_translation_entity_types', NULL);
|
||||
$row->setSourceProperty('entity_translation_entity_types', $entity_translation_entity_types);
|
||||
return parent::prepareRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['type']['type'] = 'string';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\language\Plugin\migrate\source\d7;
|
||||
|
||||
use Drupal\taxonomy\Plugin\migrate\source\d7\Vocabulary;
|
||||
|
||||
/**
|
||||
* Drupal 7 i18n vocabularies source from database.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d7_language_content_settings_taxonomy_vocabulary",
|
||||
* source_module = "i18n_taxonomy"
|
||||
* )
|
||||
*/
|
||||
class LanguageContentSettingsTaxonomyVocabulary extends Vocabulary {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$query = parent::query();
|
||||
if ($this->getDatabase()
|
||||
->schema()
|
||||
->fieldExists('taxonomy_vocabulary', 'i18n_mode')) {
|
||||
$query->addField('v', 'language');
|
||||
$query->addField('v', 'i18n_mode');
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
$fields = parent::fields();
|
||||
$fields['language'] = $this->t('i18n language');
|
||||
$fields['i18n_mode'] = $this->t('i18n mode');
|
||||
return $fields;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
// @codingStandardsIgnoreFile
|
||||
|
||||
/**
|
||||
* This file was generated via php core/scripts/generate-proxy-class.php 'Drupal\language\LanguageConverter' "core/modules/language/src".
|
||||
*/
|
||||
|
||||
namespace Drupal\language\ProxyClass {
|
||||
|
||||
/**
|
||||
* Provides a proxy class for \Drupal\language\LanguageConverter.
|
||||
*
|
||||
* @see \Drupal\Component\ProxyBuilder
|
||||
*/
|
||||
class LanguageConverter implements \Drupal\Core\ParamConverter\ParamConverterInterface
|
||||
{
|
||||
|
||||
use \Drupal\Core\DependencyInjection\DependencySerializationTrait;
|
||||
|
||||
/**
|
||||
* The id of the original proxied service.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $drupalProxyOriginalServiceId;
|
||||
|
||||
/**
|
||||
* The real proxied service, after it was lazy loaded.
|
||||
*
|
||||
* @var \Drupal\language\LanguageConverter
|
||||
*/
|
||||
protected $service;
|
||||
|
||||
/**
|
||||
* The service container.
|
||||
*
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Constructs a ProxyClass Drupal proxy object.
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
||||
* The container.
|
||||
* @param string $drupal_proxy_original_service_id
|
||||
* The service ID of the original service.
|
||||
*/
|
||||
public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container, $drupal_proxy_original_service_id)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy loads the real service from the container.
|
||||
*
|
||||
* @return object
|
||||
* Returns the constructed real service.
|
||||
*/
|
||||
protected function lazyLoadItself()
|
||||
{
|
||||
if (!isset($this->service)) {
|
||||
$this->service = $this->container->get($this->drupalProxyOriginalServiceId);
|
||||
}
|
||||
|
||||
return $this->service;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function convert($value, $definition, $name, array $defaults)
|
||||
{
|
||||
return $this->lazyLoadItself()->convert($value, $definition, $name, $defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies($definition, $name, \Symfony\Component\Routing\Route $route)
|
||||
{
|
||||
return $this->lazyLoadItself()->applies($definition, $name, $route);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user