It can not be done using core or contrib
To present 404 page not found (might create bad UX)
You could do something like this in your own module MODULENAME.module
file
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Drupal\Core\Language\LanguageInterface;
/**
* Implements hook_entity_view().
*/
function MODULENAME_entity_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
$lang = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
$type_id = $entity->getEntityTypeId();
$translated = $entity->hasTranslation($lang);
if ($type_id = "node" && $view_mode == "full" && !$translated) {
throw new NotFoundHttpException();
}
}
but once you go to the not found page the language switcher will not be able to take you back to the translated page though
To Hide untranslated languages in language switcher
There is a core issue about this and solutions are worked out but until it is solved you could create your own module
/**
* @file
* Hide language switcher links for untranslated languages on an entity.
*/
use Drupal\Core\Entity\ContentEntityInterface;
/**
* Implements hook_language_switch_links_alter().
*/
function MODULENAME_language_switch_links_alter(array &$links, $type, $path) {
if ($entity = MODULENAME_get_page_entity()) {
$new_links = array();
foreach ($links as $lang_code => $link) {
try {
if ($entity->getTranslation($lang_code)->access('view')) {
$new_links[$lang_code] = $link;
}
}
catch (\InvalidArgumentException $e) {
// This language is untranslated so do not add it to the links.
}
}
$links = $new_links;
// If we're left with less than 2 links, then there's nothing to switch.
// Hide the language switcher.
if (count($links) < 2) {
$links = array();
}
}
}
/**
* Retrieve the current page entity.
*
* @return Drupal\Core\Entity\ContentEntityInterface
* The retrieved entity, or FALSE if none found.
*/
function MODULENAME_get_page_entity() {
$params = \Drupal::routeMatch()->getParameters()->all();
$entity = reset($params);
if ($entity instanceof ContentEntityInterface) {
return $entity;
}
return FALSE;
}
Above solution is taken from this post
To Hide untranslated menu items
You can use menu_multilingual which gives option when you create/edit a menu block to hide untraslated items.