123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- <?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 already in the process of language initialization.
- *
- * @var bool
- */
- protected $initializing = FALSE;
- /**
- * {@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();
- }
- /**
- * {@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 (!$this->initializing) {
- $this->initializing = TRUE;
- $negotiation = $this->negotiator->initializeType($type);
- $this->negotiatedLanguages[$type] = reset($negotiation);
- $this->negotiatedMethods[$type] = key($negotiation);
- $this->initializing = FALSE;
- }
- // If the current interface language needs to be retrieved during
- // initialization we return the system language. This way string
- // translation calls happening during initialization will return the
- // original strings which can be translated by calling them again
- // afterwards. This can happen for instance while parsing negotiation
- // method definitions.
- elseif ($type == 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];
- }
- }
- }
|