Translatable labels for menu links

Created on 30 March 2024, 8 months ago
Updated 18 July 2024, 4 months ago

Thanks for this very useful module !

I would like to suggest making the label of the language links in the main menu configurable and translatable.

Currently, the module adds links with labels such as "English" and "Deutsch", I would like to be able to display "en" and "de" instead.

Feature request
Status

Active

Version

1.0

Component

Code

Created by

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @drupaldope
  • 🇩🇪Germany FeyP

    Thanks for filing this issue.

    We don't actually provide the titles of the links in the module. The module uses the links (including the link title) from Drupal Core's language switcher and makes them available for display as menu links.

    If you want to change the language switch links, including the link titles, there are a bunch of contributed modules available that you should be able to install in addition to this module that allow for various alterations of the language switch links. Alternatively, you can use hook_language_switch_links_alter() in a custom module.

    For your specific request, there is also an example in the FAQ on the project page that I think does exactly what you asked for by just changing a template file in your theme.

  • Thank you for your very fast reply.

    I wouldn't want to do this via template, because menus are used in several places on the site, and it's in general less scalable than a preprocess function or similar.

    The problem is, neither menu preprocess nor the hook you mentioned seem to work.

    I have tried different things in the theme's .theme file, such as

    function mytheme_language_switch_links_alter(array &$links) {
        if (isset($links['en']['link'])) {
          $links['en']['link']['#title']->setLinkTitle('en');
        }
    }

    or

    function mytheme_language_switch_links_alter(array &$links) {
      if (isset($links['en']['link']['#title'])) { 
        $links['en']['link']['#title'] = 'en'; 
      }
    }

    and none seems to work

  • 🇩🇪Germany FeyP

    Sorry, it took me a while to get back to this issue.

    I'm not really sure how the patch posted in #4 would help here and I think it also doesn't apply to the latest version of the module.

    Unfortunately, I can't say why your preprocessing function doesn't work without seeing the code. But hook_language_switch_links_alter() is a module hook. It is not invoked for themes. So if you want to use this hook, you have to do it in a custom module, not in a theme.

    Alternatively, there are a lot of contributed modules out there that can be used to modify the links in several ways, which can be used in combination with this module.

    For example, https://www.drupal.org/project/language_switcher_extended has an option called "Show language code", which does exactly what you want to do.

    There is also https://www.drupal.org/project/language_switcher_langcode , but that module uses all uppercase instead of lower case.

    There are probably more modules you could use in combination with this one that provide this feature, I stopped looking after I found those two.

  • @FeyP
    thank you for your answer.

    things get more complicated when I want to display "en" instead of "en-gb".

    the problem with the other modules is that they do not add the links to an existing menu.

    so I am left without a solution at the moment.

  • my current workaround is making two dummy links in the main menu and then replace label and url with the correct values:

    /**
     * use two dummy links in the main menu as language links
     */
    /*
    use Drupal\Core\Url;
    function ***mytheme***_preprocess_menu__main(&$variables) {
      // Retrieve the current route.
      $route_match = \Drupal::routeMatch();
      $route_name = $route_match->getRouteName();
    
      // Get the list of enabled languages.
      $enabled_languages = \Drupal::languageManager()->getLanguages();
    
      // Get the current language.
      $current_language = \Drupal::languageManager()->getCurrentLanguage()->getId();
    
      // Define an array to store shortened language codes and their associated URLs.
      $menulangArray = [];
    
      // Check if the current route corresponds to a node page.
      if ($route_name == 'entity.node.canonical') {
        // Retrieve the current node.
        $node = $route_match->getParameter('node');
    
        // Proceed only if a node is found.
        if ($node) {
          // Iterate through the enabled languages.
          foreach ($enabled_languages as $langcode => $language) {
            // Check if the current node has a translation in the language.
            if ($node->hasTranslation($langcode)) {
              // Get the translated node in the current language.
              $node_translation = $node->getTranslation($langcode);
              if ($node_translation->isPublished()) {
                // If the translation is published, add its URL to menulangArray.
                $url = $node_translation->toUrl()->setOption('language', $language)->setAbsolute()->toString();
                // Shorten the language code to 2 characters.
                $shortLangcode = substr($langcode, 0, 2);
                // Check if the language is the current language and add "is-active" class.
                $class = ($current_language == $langcode) ? 'is-active' : '';
                $menulangArray[$shortLangcode] = ['url' => $url, 'class' => $class];
              }
            }
          }
        }
      } else {
        // For other entities like views or custom pages, generate language-specific URLs.
        foreach ($enabled_languages as $langcode => $language) {
          // Get the current path.
          $current_path = \Drupal::service('path.current')->getPath();
          
          // Generate language-specific URLs.
          $url = Url::fromUserInput($current_path, ['language' => $language])->setAbsolute()->toString();
          // Shorten the language code to 2 characters.
          $shortLangcode = substr($langcode, 0, 2);
          // Check if the language is the current language and add "is-active" class.
          $class = ($current_language == $langcode) ? 'is-active' : '';
          $menulangArray[$shortLangcode] = ['url' => $url, 'class' => $class];
        }
      }
    
      // Iterate through main menu items and replace the URLs.
      foreach ($variables['items'] as &$item) {
        if (isset($item['title'])) {
          // Iterate through menulangArray to find a matching key.
          foreach ($menulangArray as $shortLangcode => $data) {
            if ($item['title'] === $shortLangcode) {
              // Preserve existing attributes of menu items.
              $attributes = isset($item['attributes']) ? $item['attributes'] : [];
    
              // Replace the URL of the menu item with the generated URL.
              $item['url'] = Url::fromUri($data['url']);
              
              // Add the "is-active" class if it's the current language.
              $attributes['class'][] = $data['class'];
    
              // Set the updated attributes.
              $item['attributes'] = $attributes;
    
              // Break out of the loop once a match is found.
              break;
            }
          }
        }
      }
    }
  • 🇩🇪Germany FeyP

    With this module enabled, this preprocess function could be a lot simpler. The following worked for me:

        if (isset($variables['items']['language_switcher_menu.language_switcher_link:en-gb'])) {
          $variables['items']['language_switcher_menu.language_switcher_link:en-gb']['title'] = 'en';
        }
    
  • 🇩🇪Germany FeyP

    Alternatively, you could use a more general approach as well, similar to what is in the example template file:

        foreach ($variables['items'] as $id => $item) {
          if (str_starts_with($id, 'language_switcher_menu.language_switcher_link')) {
            $langcode = $item['original_link']->getOptions()['language']->getId();
            $title = match ($langcode) {
              'en-gb' => 'en',
              default => strtolower($langcode),
            };
            $variables['items'][$id]['title'] = $title;
          }
        }
    
  • I tried that of course, but for some reason I was unable to change the menu links from a preprocess function from within mytheme.theme

Production build 0.71.5 2024