How do I alter a language item link? I am looking to modify the default output of Drupal 8 of links in a certain situation, the language switcher. Ideally I want to be able to do that in a twig template and not have to produce a massive php file that overwwrites that whole link generation system and add a BIG if for the cases I need to overwrite.

I think the output layout should be controllable in the twig template similar to this answer (this answer outputs the same url for every languages)

I am using bootstrap as a base theme so i'd like to use the bootstrap markup for my links (btn btn-primary, dropdown)

Here is my code I am trying to make links--language-block.html.twig

{%- if links|length == 2 -%}
    {# show only alternate language button #}
    {%- for key, item in links -%}
        {%- if not item.attributes['#options']['set_active_class'] -%} {# &lt;--- this is always true!? #}
            &lt;a href="{{ item.link['#url'] }}" class="btn btn-primary"&gt;{{ item.link['#title'] }}&lt;/a&gt; {# #url is always the same what ever the language it is pointing to! #}
        {%- endif -%}
    {%- endfor -%}
{%- elseif links|length &gt; 2  -%}
    {# show selected language in button and other languages in drop down #}
    &lt;button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown"&gt;{{ selectedLanguage }}&lt;span class="caret"&gt;&lt;/span&gt;&lt;/button&gt;
    &lt;ul class="dropdown-menu"&gt;
        {% for key, item in links %}
            {%- if not item.attributes['#options']['set_active_class'] -%} {# &lt;--- this is always true!? #}
            &lt;li&gt;&lt;a href="{{ item.link['#url'] }}"&gt;{{ item.link['#title'] }}&lt;/a&gt;&lt;/li&gt;
            {% endif %}
        {% endfor %}
    &lt;/ul&gt;
{%- endif -%}


Anyone has an idea on how to do this?
Ok I found 2 ways to do that.

1. In a custom theme

You can alter variables in your my_theme.theme file. You need to figure out the name of the function you need. ex: my_theme_preprocess_twig_file(), in my case I needed my_theme_preprocess_links__language_block() You need to take the twig file name and replace all - for _.

my_theme.theme:

function my_theme_preprocess_links__language_block(&amp;$variables) {
  $currentLanguageCode = \Drupal::languageManager()
    -&gt;getCurrentLanguage()
    -&gt;getId();
  // replace key of active language with 'activeLink'
  foreach ($variables['links'] as $i =&gt; $link) {
    /** @var \Drupal\language\Entity\ConfigurableLanguage $linkLanguage */
    $linkLanguage = $link['link']['#options']['language'];
    if ($currentLanguageCode == $linkLanguage-&gt;get('id')) {
      $variables['links']['activeLink'] = $link;
      unset($variables['links'][$i]);
    }
  }
  // if there is only 2 languages remove active one
  if (sizeof($variables['links']) == 2) {
    unset($variables['links']['activeLink']);
    // give class 'btn btn-primary' to alternate language
    /** @var \Drupal\Core\Url $alternate */
    $alternate = current($variables['links']);
    $alternate['link']['#options']['attributes']['class'][] = 'btn';
    $alternate['link']['#options']['attributes']['class'][] = 'btn-primary';
    $variables['links'] = [$alternate];
  }
}


2. In a custom module

You can also create a module that will change those same variables. There is a big difference in the values of the variables because this preprocess happened earlier in the flow. The name of the function is also quite different, ex: my_module_api_to_modify_alter(). In my case I needed to modify the language_switch_links from the language.api.php. You can find all alter function by searching for *.api.php files in drupal 8. Those are there as a reference for exactly that.

my_module.module:

function my_module_language_switch_links_alter(&amp;$variables) {
  $currentLanguageCode = \Drupal::languageManager()
    -&gt;getCurrentLanguage()
    -&gt;getId();
  // replace key of active language with 'activeLink'
  foreach ($variables as $i =&gt; $link) {
    /** @var \Drupal\language\Entity\ConfigurableLanguage $linkLanguage */
    $linkLanguage = $link['language'];
    if ($currentLanguageCode == $linkLanguage-&gt;get('id')) {
      $variables['activeLink'] = $link;
      unset($variables[$i]);
    }
  }
  // if there is only 2 languages remove active one
  if (sizeof($variables) == 2) {
    unset($variables['activeLink']);
    // give class 'btn btn-primary' to alternate language
    /** @var \Drupal\Core\Url $alternate */
    $alternate = current($variables);
    $alternate['attributes']['class'][] = 'btn';
    $alternate['attributes']['class'][] = 'btn-primary';
    $variables = [$alternate];
  }
}


And my Twig template for both cases links--language-block.html.twig:

{% if links -%}
    {%- if links|length == 1 -%}
        {# show only alternate language button #}
        {{ (links|first).link }}
    {%- else -%}
        {# show selected language in button and other languages in drop down #}
        &lt;button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown"&gt;{{ links['activeLink'].text }} &lt;span class="caret"&gt;&lt;/span&gt;&lt;/button&gt;
        &lt;ul class="dropdown-menu"&gt;
            {% for key, item in links %}
                {% if key is not same as("activeLink") %}
                &lt;li&gt;{{ item.link }}&lt;/li&gt;
                {% endif %}
            {% endfor %}
        &lt;/ul&gt;
    {%- endif -%}
{%- endif %}