What are Menus?

Menus, to be brief, are a collection of links used to navigate through a site. With it, developers are able to create and customize a hierarchical list of links using Drupal's powerful Block feature. A new block is created whenever a new menu is created. This can be incredibly useful for things like headers, footers or sidebars where users would expect static links to help them navigate the site. And since Drupal automatically turns them into blocks, they can be placed just about anywhere.

Despite all that, however, Menus still have a few small issues here and there. Which brings us to my current issue with them.

Multilanguage sites

Most entities in Drupal come packaged with some method of checking whether they have translations. Nodes and terms for example, have the useful hasTranslation() and getTranslation() functions to check and retrieve translations, respectively. While that's incredibly helpful for those entities, menus have no such luck, because menus can be translated, they have no such functions. If specifications for a site calls for menu links to be shown whether they have translations for the language the site is currently being viewed in, Menus will be shown regardless if translated or not, making things rather difficult. So what's a developer to do?

Hooks

Hooks allow modules to alter the behavior of other Drupal modules, themes, and core. With this method of manipulating data, altering Menus becomes possible, and by extension getting menu link translations and removing those that do not have any. So assuming you're using hooks on your theme, with hook_preprocess_menu, use this code:

function hook_load_link_entity_by_link(MenuLinkInterface $menuLinkContentPlugin) {
  $entity = NULL;
  if ($menuLinkContentPlugin instanceof MenuLinkContent) {
    list($entity_type, $uuid) = explode(':', $menuLinkContentPlugin->getPluginId(), 2);
    $entity = \Drupal::entityTypeManager()->getStorage('menu_link_content')->loadByProperties(['uuid' => $uuid]);
    $entity = reset($entity);
  }
  return $entity;
}

What the above snippet is doing exactly, is retrieving the menu link object first by checking if the arguments passed is a valid Menu link, getting the link's universally unique identifier (uuid), and returning the actual content of the menu link. This is important since menu link entity doesn't have fields, and you will be needing the entity for communicating with the database. After that, it is time for checking and retrieving the actual translation for the menu link. To do that, you will be using the following function:

function hook_load_translation_status($id, $langcode){
  $database = \Drupal::database();
  $query = $database->select('menu_link_content_data','data');
  $query->addField('data', 'content_translation_status');
  $query->condition('data.id', $id);
  $query->condition('data.langcode', $langcode);
  $result = $query->execute()->fetchAll();
  if(empty($result)){
    return FALSE;
  } else{
    return $result[0]->content_translation_status;
  }
}

The above function accepts two parameters, the menu link id, and a language code. By passing the menu link id and the langcode you would like to check a translation for, Drupal will check if the link has that language's translation, and if it gets it, returns true, otherwise if not, it returns false. This way, you'll have the necessary info you need to take action. Perhaps you could hide the menu item, or just unset it so it gets skipped.

The above code is just a small side step to getting around this specific issue with Drupal's menus. While the snippets shown are only for obtaining the translation statuses of menu links, they could also possibly jumping-off points for retrieving other pertinent data not covered here. Hopefully, Drupal's maintainers will provide updates so we do not have to do this in the future. Despite all that, however, Menus are still a very useful, helpful tool for static links.