Language switcher links are hidden for anonymous users I'm trying to add a language switcher to a Drupal 8 site menu (it's switching between English and Spanish). I have the links working when logged in as admin but they're not shown for anonymous users, even though the language switcher block displays fine, as well as adding a language switcher through twig. However, I'd really like to get this working and be able to add it to menus through a custom module.

As an anonymous user I am able to view the translated content (by clicking the Default Drupal language switcher options) but these links that are created are not rendered.

Here's my code for the custom module:

MYMODULE.links.menu.yml

MYMODULE.switch:
  class: Drupal\MYMODULE\Plugin\Menu\MenuLanguageLink
  menu_name: MENUNAME
  route_name: '&lt;current&gt;'
  options:
    attributes:
      hreflang: 'en'
MYMODULE.switch_2:
  class: Drupal\MYMODULE\Plugin\Menu\MenuLanguageLink
  menu_name: MENU2NAME
  route_name: '&lt;current&gt;'
  options:
    attributes:
      hreflang: 'es'


/src/Plugin/Menu/MenuLanguageLink.php:

&lt;?php
/**
 * @file
 * Contains \Drupal\MYMODULE\Plugin\Menu\LanguageSwitchController.
 */
namespace Drupal\MYMODULE\Plugin\Menu;

use Drupal\Core\Menu\MenuLinkBase;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class MenuLanguageLink extends MenuLinkBase implements ContainerFactoryPluginInterface {

  /** @var array Available languages */
  protected $languages = [];

  /**
   * {@inheritdoc}
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    LanguageManagerInterface $language_manager
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this-&gt;languages = $language_manager-&gt;getLanguages();
  }

  /**
   * Dependency injection.
   */
  public static function create(
    ContainerInterface $container,
    array $configuration,
    $plugin_id,
    $plugin_definition
  ) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container-&gt;get('language_manager')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function updateLink(array $new_definition_values, $persist) {
    return $this-&gt;pluginDefinition;
  }

  /**
   * {@inheritdoc}
   */
  public function getOptions() {
    $options = parent::getOptions();
    $langcode = $options['attributes']['hreflang'];

    if (isset($this-&gt;languages[$langcode])) {
      $options += ['language' =&gt; $this-&gt;languages[$langcode]];
    }

    return $options;
  }

  /**
   * {@inheritdoc}
   */
  public function getTitle() {
    $langcode = $this-&gt;getOptions()['attributes']['hreflang'];
    if ($langcode == 'es') {
      return 'Español';
    } else {
      return $this-&gt;languages[$langcode]-&gt;getName();
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getDescription() {
    return $this-&gt;t('Switch Language');
  }

}
After installing your module, I can confirm the behavior that you're seeing.

When I change the route to &lt;front&gt; I am able to see the links as anonymous. 

What I know is that the menu system automatically checks if the user has access or permission before it displays the links.

So my first thought is there must be a Core bug with this &lt;current&gt; route. 

When I check /core/modules/system/system.routing.yml all I see is:

'&lt;current&gt;':
  path: '&lt;current&gt;'


Which is interesting that this route doesn't have an access nor a permission key.

In /admin/people/permissions if I check this permission:


  Link to any page
  
  Warning: Give to trusted roles only; this permission has security implications. This allows to bypass access checking when linking to
  internal paths.


I can see the links as anonymous, but obivously we do not want to this.



When I try to add &lt;current&gt; via the UI, I cannot; even though, &lt;front&gt; is acceptable.



Therefore, my best educated guess, seems to me that the &lt;current&gt; route is not designed to be used in Menu links because it doesn't have an access key nor permission key. Also, this might explain why the language switcher is a block and not a menu link.

 

However, there is work around for this if you pro-grammatically print the menu and not perform an access check, but there is a caveat. You would have to make sure there is no link in this menu that anonymous is not suppose to see.

As per code provided in Menu system, "Rendering" section, comment out the access check part. 

Example:

    $menu_tree = \Drupal::menuTree();
    $menu_name = 'MENUNAME';

    // Build the typical default set of menu tree parameters.
    $parameters = $menu_tree
      -&gt;getCurrentRouteMenuTreeParameters($menu_name);

    // Load the tree based on this set of parameters.
    $tree = $menu_tree
      -&gt;load($menu_name, $parameters);

    // Transform the tree using the manipulators you want.
    $manipulators = array(
//    // Only show links that are accessible for the current user.
//    array(
//      'callable' =&gt; 'menu.default_tree_manipulators:checkAccess',
//    ),
      // Use the default sorting of menu links.
      array(
        'callable' =&gt; 'menu.default_tree_manipulators:generateIndexAndSort',
      ),
    );
    $tree = $menu_tree
      -&gt;transform($tree, $manipulators);

    // Finally, build a renderable array from the transformed tree.
    $menu = $menu_tree
      -&gt;build($tree);
    $menu_html = drupal_render($menu);

    print $menu_html; // print the menu


If the above does not suffice, because of the caveat, you might want to look into


  Potentially write a custom menu tree manipulator, see
  \Drupal\Core\Menu\DefaultMenuLinkTreeManipulators for examples. This
  is only necessary if you want to do things like adding extra metadata
  to rendered links to display icons next to them.
  
  source: Menu System


I believe here you will be able to target specifically the language menu links and make Drupal skip the access check only on those specific language links which would be the best solution.