Drupal - Switch a language programatically
In a custom module, you should use a custom LanguageNegotiator and assign it to your LanguageManager, using LanguageManager->setNegotiator();
1) First, you must define a CustomLanguageNegotiator, which overwrites initializeType() method. That method should return a Language object, which you can externally define using a custom setLanguageCode() method:
<?php
namespace Drupal\custom_module\Language;
/**
* Class responsible for performing language negotiation.
*/
class CustomLanguageNegotiator extends \Drupal\language\LanguageNegotiator {
var $languageCode = NULL;
/**
* {@inheritdoc}
*/
public function initializeType($type) {
$language = NULL;
$method_id = static::METHOD_ID;
$availableLanguages = $this->languageManager->getLanguages();
if ($this->languageCode && isset($availableLanguages[$this->languageCode])) {
$language = $availableLanguages[$this->languageCode];
}
else {
// If no other language was found use the default one.
$language = $this->languageManager->getDefaultLanguage();
}
return array($method_id => $language);
}
/**
* @param string $languageCode
*/
public function setLanguageCode($languageCode)
{
$this->languageCode = $languageCode;
}
}
2) Define your CustomLanguageNegotiator as a service, in your custom module service file, "custom_module.services.yml":
custom_module.language_negotiator:
class: Drupal\custom_module\Language\CustomLanguageNegotiator
arguments: ['@language_manager', '@plugin.manager.language_negotiation_method', '@config.factory', '@settings', '@request_stack']
calls:
- [initLanguageManager]
3) Rebuild your cache and use your CustomLanguageNegotiator like this:
$languageManager = \Drupal::languageManager();
$customLanguageNegotiator = \Drupal::service('custom.language_negotiator');
$languageManager->setNegotiator($customLanguageNegotiator);
// Set new language by its langcode
$languageManager->reset(); // Needed to re-run language negotiation
$languageManager->getNegotiator()->setLanguageCode('fr'); // 'en', 'es', 'fr', etc
// Run any code, and its output will be displayed in french
// ...
$languageManager->reset();
$languageManager->getNegotiator()->setLanguageCode('en');
// Run any code, and its output will be displayed in english
// ...
// Do not forget to set language to its initial value
$languageManager->reset();
$languageManager->getNegotiator()->setLanguageCode('XX');
You can change the current language to $lang_code
with the following code.
$lang_code = 'fr';
\Drupal::configFactory()->getEditable('system.site')->set('default_langcode', $lang_code)->save();
Best way is to create a new Language Negotiation plugin, create a new file in a custom module in the folder src/Plugin/LanguageNegotiation
, to keep with naming convention I called my class LanguageNegotiationXXX, the contents of this file should be:
<?php
namespace Drupal\my_module\Plugin\LanguageNegotiation;
use Drupal\language\LanguageNegotiationMethodBase;
use Symfony\Component\HttpFoundation\Request;
/**
* Custom class for identifying language.
*
* @LanguageNegotiation(
* id = Drupal\my_module\Plugin\LanguageNegotiation\LanguageNegotiationXXX::METHOD_ID,
* weight = -99,
* name = @Translation("XXX Language Switching"),
* description = @Translation("Language based on XXX."),
* )
*/
class LanguageNegotiationXXX extends LanguageNegotiationMethodBase {
/**
* The language negotiation method id.
*/
const METHOD_ID = 'language-XXX';
/**
* {@inheritdoc}
*/
public function getLangcode(Request $request = NULL) {
$langcode = NULL;
// Custom logic goes here, but should set a langcode such as fr
return $langcode;
}
}
Enable module or clear caches for this to take effect, go to admin/config/regional/language/detection
and enable your new detection method.
This worked perfectly for me with all users, all the other methods I tried didn't work with anonymous users.