Chris@0: invalidateContainer(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Constructs a new ConfigurableLanguageManager object. Chris@0: * Chris@0: * @param \Drupal\Core\Language\LanguageDefault $default_language Chris@0: * The default language service. Chris@0: * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory Chris@0: * The configuration factory service. Chris@0: * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler Chris@0: * The module handler service. Chris@0: * @param \Drupal\language\Config\LanguageConfigFactoryOverrideInterface $config_override Chris@0: * The language configuration override service. Chris@0: * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack Chris@0: * The request stack object. Chris@0: */ Chris@0: public function __construct(LanguageDefault $default_language, ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, LanguageConfigFactoryOverrideInterface $config_override, RequestStack $request_stack) { Chris@0: $this->defaultLanguage = $default_language; Chris@0: $this->configFactory = $config_factory; Chris@0: $this->moduleHandler = $module_handler; Chris@0: $this->configFactoryOverride = $config_override; Chris@0: $this->requestStack = $request_stack; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function init() { Chris@0: if (!$this->initialized) { Chris@0: foreach ($this->getDefinedLanguageTypes() as $type) { Chris@0: $this->getCurrentLanguage($type); Chris@0: } Chris@0: $this->initialized = TRUE; Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function isMultilingual() { Chris@0: return count($this->getLanguages(LanguageInterface::STATE_CONFIGURABLE)) > 1; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getLanguageTypes() { Chris@0: $this->loadLanguageTypesConfiguration(); Chris@0: return $this->languageTypes['configurable']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getDefinedLanguageTypes() { Chris@0: $this->loadLanguageTypesConfiguration(); Chris@0: return $this->languageTypes['all']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Retrieves language types from the configuration storage. Chris@0: * Chris@0: * @return array Chris@0: * An array of language type names. Chris@0: */ Chris@0: protected function loadLanguageTypesConfiguration() { Chris@0: if (!$this->languageTypes) { Chris@0: $this->languageTypes = $this->configFactory->get('language.types')->get() ?: ['configurable' => [], 'all' => parent::getLanguageTypes()]; Chris@0: } Chris@0: return $this->languageTypes; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getDefinedLanguageTypesInfo() { Chris@0: if (!isset($this->languageTypesInfo)) { Chris@0: $defaults = parent::getDefinedLanguageTypesInfo(); Chris@0: Chris@0: $info = $this->moduleHandler->invokeAll('language_types_info'); Chris@0: $language_info = $info + $defaults; Chris@0: Chris@0: // Let other modules alter the list of language types. Chris@0: $this->moduleHandler->alter('language_types_info', $language_info); Chris@0: $this->languageTypesInfo = $language_info; Chris@0: } Chris@0: return $this->languageTypesInfo; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function saveLanguageTypesConfiguration(array $values) { Chris@0: $config = $this->configFactory->getEditable('language.types'); Chris@0: if (isset($values['configurable'])) { Chris@0: $config->set('configurable', $values['configurable']); Chris@0: } Chris@0: if (isset($values['all'])) { Chris@0: $config->set('all', $values['all']); Chris@0: } Chris@18: $config->save(TRUE); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getCurrentLanguage($type = LanguageInterface::TYPE_INTERFACE) { Chris@0: if (!isset($this->negotiatedLanguages[$type])) { Chris@0: // Ensure we have a valid value for this language type. Chris@0: $this->negotiatedLanguages[$type] = $this->getDefaultLanguage(); Chris@0: Chris@0: if ($this->negotiator && $this->isMultilingual()) { Chris@17: if (!isset($this->initializing[$type])) { Chris@17: $this->initializing[$type] = TRUE; Chris@0: $negotiation = $this->negotiator->initializeType($type); Chris@0: $this->negotiatedLanguages[$type] = reset($negotiation); Chris@0: $this->negotiatedMethods[$type] = key($negotiation); Chris@17: unset($this->initializing[$type]); Chris@0: } Chris@0: // If the current interface language needs to be retrieved during Chris@0: // initialization we return the system language. This way string Chris@0: // translation calls happening during initialization will return the Chris@0: // original strings which can be translated by calling them again Chris@0: // afterwards. This can happen for instance while parsing negotiation Chris@0: // method definitions. Chris@0: elseif ($type == LanguageInterface::TYPE_INTERFACE) { Chris@0: return new Language(['id' => LanguageInterface::LANGCODE_SYSTEM]); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: return $this->negotiatedLanguages[$type]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function reset($type = NULL) { Chris@0: if (!isset($type)) { Chris@0: $this->initialized = FALSE; Chris@0: $this->negotiatedLanguages = []; Chris@0: $this->negotiatedMethods = []; Chris@0: $this->languageTypes = NULL; Chris@0: $this->languageTypesInfo = NULL; Chris@0: $this->languages = []; Chris@0: if ($this->negotiator) { Chris@0: $this->negotiator->reset(); Chris@0: } Chris@0: } Chris@0: elseif (isset($this->negotiatedLanguages[$type])) { Chris@0: unset($this->negotiatedLanguages[$type]); Chris@0: unset($this->negotiatedMethods[$type]); Chris@0: } Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getNegotiator() { Chris@0: return $this->negotiator; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function setNegotiator(LanguageNegotiatorInterface $negotiator) { Chris@0: $this->negotiator = $negotiator; Chris@0: $this->initialized = FALSE; Chris@0: $this->negotiatedLanguages = []; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getLanguages($flags = LanguageInterface::STATE_CONFIGURABLE) { Chris@0: // If a config override is set, cache using that language's ID. Chris@0: if ($override_language = $this->getConfigOverrideLanguage()) { Chris@0: $static_cache_id = $override_language->getId(); Chris@0: } Chris@0: else { Chris@0: $static_cache_id = $this->getCurrentLanguage()->getId(); Chris@0: } Chris@0: Chris@0: if (!isset($this->languages[$static_cache_id][$flags])) { Chris@0: // Initialize the language list with the default language and default Chris@0: // locked languages. These cannot be removed. This serves as a fallback Chris@0: // list if this method is invoked while the language module is installed Chris@0: // and the configuration entities for languages are not yet fully Chris@0: // imported. Chris@0: $default = $this->getDefaultLanguage(); Chris@0: $languages = [$default->getId() => $default]; Chris@0: $languages += $this->getDefaultLockedLanguages($default->getWeight()); Chris@0: Chris@0: // Load configurable languages on top of the defaults. Ideally this could Chris@0: // use the entity API to load and instantiate ConfigurableLanguage Chris@0: // objects. However the entity API depends on the language system, so that Chris@0: // would result in infinite loops. We use the configuration system Chris@0: // directly and instantiate runtime Language objects. When language Chris@0: // entities are imported those cover the default and locked languages, so Chris@0: // site-specific configuration will prevail over the fallback values. Chris@0: // Having them in the array already ensures if this is invoked in the Chris@0: // middle of importing language configuration entities, the defaults are Chris@0: // always present. Chris@0: $config_ids = $this->configFactory->listAll('language.entity.'); Chris@0: foreach ($this->configFactory->loadMultiple($config_ids) as $config) { Chris@0: $data = $config->get(); Chris@0: $data['name'] = $data['label']; Chris@0: $languages[$data['id']] = new Language($data); Chris@0: } Chris@0: Language::sort($languages); Chris@0: Chris@0: // Filter the full list of languages based on the value of $flags. Chris@0: $this->languages[$static_cache_id][$flags] = $this->filterLanguages($languages, $flags); Chris@0: } Chris@0: Chris@0: return $this->languages[$static_cache_id][$flags]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getNativeLanguages() { Chris@0: $languages = $this->getLanguages(LanguageInterface::STATE_CONFIGURABLE); Chris@0: $natives = []; Chris@0: Chris@0: $original_language = $this->getConfigOverrideLanguage(); Chris@0: Chris@0: foreach ($languages as $langcode => $language) { Chris@0: $this->setConfigOverrideLanguage($language); Chris@0: $natives[$langcode] = ConfigurableLanguage::load($langcode); Chris@0: } Chris@0: $this->setConfigOverrideLanguage($original_language); Chris@0: Language::sort($natives); Chris@0: return $natives; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function updateLockedLanguageWeights() { Chris@0: // Get the weight of the last configurable language. Chris@0: $configurable_languages = $this->getLanguages(LanguageInterface::STATE_CONFIGURABLE); Chris@0: $max_weight = end($configurable_languages)->getWeight(); Chris@0: Chris@0: $locked_languages = $this->getLanguages(LanguageInterface::STATE_LOCKED); Chris@0: // Update locked language weights to maintain the existing order, if Chris@0: // necessary. Chris@0: if (reset($locked_languages)->getWeight() <= $max_weight) { Chris@0: foreach ($locked_languages as $language) { Chris@0: // Update system languages weight. Chris@0: $max_weight++; Chris@0: ConfigurableLanguage::load($language->getId()) Chris@0: ->setWeight($max_weight) Chris@0: ->save(); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getFallbackCandidates(array $context = []) { Chris@0: if ($this->isMultilingual()) { Chris@0: $candidates = []; Chris@0: if (empty($context['operation']) || $context['operation'] != 'locale_lookup') { Chris@0: // If the fallback context is not locale_lookup, initialize the Chris@0: // candidates with languages ordered by weight and add Chris@0: // LanguageInterface::LANGCODE_NOT_SPECIFIED at the end. Interface Chris@0: // translation fallback should only be based on explicit configuration Chris@0: // gathered via the alter hooks below. Chris@0: $candidates = array_keys($this->getLanguages()); Chris@0: $candidates[] = LanguageInterface::LANGCODE_NOT_SPECIFIED; Chris@0: $candidates = array_combine($candidates, $candidates); Chris@0: Chris@0: // The first candidate should always be the desired language if Chris@0: // specified. Chris@0: if (!empty($context['langcode'])) { Chris@0: $candidates = [$context['langcode'] => $context['langcode']] + $candidates; Chris@0: } Chris@0: } Chris@0: Chris@0: // Let other modules hook in and add/change candidates. Chris@0: $type = 'language_fallback_candidates'; Chris@0: $types = []; Chris@0: if (!empty($context['operation'])) { Chris@0: $types[] = $type . '_' . $context['operation']; Chris@0: } Chris@0: $types[] = $type; Chris@0: $this->moduleHandler->alter($types, $candidates, $context); Chris@0: } Chris@0: else { Chris@0: $candidates = parent::getFallbackCandidates($context); Chris@0: } Chris@0: Chris@0: return $candidates; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getLanguageSwitchLinks($type, Url $url) { Chris@0: $links = FALSE; Chris@0: Chris@0: if ($this->negotiator) { Chris@0: foreach ($this->negotiator->getNegotiationMethods($type) as $method_id => $method) { Chris@0: $reflector = new \ReflectionClass($method['class']); Chris@0: Chris@0: if ($reflector->implementsInterface('\Drupal\language\LanguageSwitcherInterface')) { Chris@0: $result = $this->negotiator->getNegotiationMethodInstance($method_id)->getLanguageSwitchLinks($this->requestStack->getCurrentRequest(), $type, $url); Chris@0: Chris@0: if (!empty($result)) { Chris@0: // Allow modules to provide translations for specific links. Chris@0: $this->moduleHandler->alter('language_switch_links', $result, $type, $url); Chris@0: $links = (object) ['links' => $result, 'method_id' => $method_id]; Chris@0: break; Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: return $links; Chris@0: } Chris@0: Chris@0: /** Chris@14: * Sets the configuration override language. Chris@14: * Chris@14: * @param \Drupal\Core\Language\LanguageInterface $language Chris@14: * The language to override configuration with. Chris@14: * Chris@14: * @return $this Chris@0: */ Chris@0: public function setConfigOverrideLanguage(LanguageInterface $language = NULL) { Chris@0: $this->configFactoryOverride->setLanguage($language); Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getConfigOverrideLanguage() { Chris@0: return $this->configFactoryOverride->getLanguage(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getLanguageConfigOverride($langcode, $name) { Chris@0: return $this->configFactoryOverride->getOverride($langcode, $name); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getLanguageConfigOverrideStorage($langcode) { Chris@0: return $this->configFactoryOverride->getStorage($langcode); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getStandardLanguageListWithoutConfigured() { Chris@0: $languages = $this->getLanguages(); Chris@0: $predefined = $this->getStandardLanguageList(); Chris@0: foreach ($predefined as $key => $value) { Chris@0: if (isset($languages[$key])) { Chris@0: unset($predefined[$key]); Chris@0: continue; Chris@0: } Chris@0: $predefined[$key] = new TranslatableMarkup($value[0]); Chris@0: } Chris@0: natcasesort($predefined); Chris@0: return $predefined; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getNegotiatedLanguageMethod($type = LanguageInterface::TYPE_INTERFACE) { Chris@0: if (isset($this->negotiatedLanguages[$type]) && isset($this->negotiatedMethods[$type])) { Chris@0: return $this->negotiatedMethods[$type]; Chris@0: } Chris@0: } Chris@0: Chris@0: }