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]; } } }