Skip to content

Commit

Permalink
feat(SLB-423): add language redirect
Browse files Browse the repository at this point in the history
  • Loading branch information
Leksat committed Jun 26, 2024
1 parent 8d5acd7 commit 30d01e1
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 6 deletions.
6 changes: 6 additions & 0 deletions packages/drupal/custom/custom.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ services:

custom.menus:
class: Drupal\custom\Menus

custom.entity_language_redirect_subscriber:
class: Drupal\custom\EventSubscriber\EntityLanguageRedirectSubscriber
arguments: ['@language_manager', '@current_route_match']
tags:
- { name: event_subscriber }
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

namespace Drupal\custom\EventSubscriber;

use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Routing\TrustedRedirectResponse;
use Drupal\Core\Url;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class EntityLanguageRedirectSubscriber implements EventSubscriberInterface {

protected LanguageManagerInterface $languageManager;

protected RouteMatchInterface $routeMatch;

public function __construct(LanguageManagerInterface $language_manager, RouteMatchInterface $route_match) {
$this->languageManager = $language_manager;
$this->routeMatch = $route_match;
}

public static function getSubscribedEvents() {
// We need this subscriber to run after the router_listener service (which
// has priority 32) so that the parameters are set into the request, but
// before the EntityCanonicalViewSubscriber one (with the priority 28). So,
// we set the priority to 30.
return [
KernelEvents::REQUEST => [
['onKernelRequest', 30],
]
];
}

public function onKernelRequest(RequestEvent $event): void {
// In case the user tries to access a node in a language entity is not
// translated to, we redirect to the entity in the original language and
// display a warning message.
if ($this->routeMatch->getRouteName() === 'entity.node.canonical') {
$entity = $this->routeMatch->getCurrentRouteMatch()->getParameter('node');
$requestedLanguageId = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
if ($entity->language()->getId() != $requestedLanguageId) {
$routeOptions = [
'language' => $entity->language(),
];
$routeParameters = [
'node' => $entity->id(),
];
$url = Url::fromRoute('entity.node.canonical', $routeParameters, $routeOptions);
// Make sure we keep any query strings.
$queryString = (string) $event->getRequest()->getQueryString();
if ($queryString !== '') {
$queryString .= '&';
}
$urlString = $url->toString() . '?' . $queryString . 'content_language_not_available=true&requested_language=' . $requestedLanguageId;

// Add the necessary cache contexts to the response, as redirect
// responses are cached as well.
$metadata = new CacheableMetadata();
$metadata->addCacheContexts(['languages:language_interface', 'url.query_args']);
$response = new TrustedRedirectResponse($urlString);
$response->addCacheableDependency($entity);
$response->addCacheableDependency($metadata);

$event->setResponse($response);
}
}
}
}
14 changes: 11 additions & 3 deletions packages/ui/src/components/Molecules/Messages.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { Html, Markup } from '@custom/schema';
import React from 'react';
import React, { ReactNode } from 'react';

// TODO: Style, add stories.

export function Messages(props: { messages: Array<string> }) {
return <div>{buildMessages(props.messages)}</div>;
export function Messages(props: {
messages: Array<string>;
messageComponents?: Array<ReactNode>;
}) {
return (
<div>
{buildMessages(props.messages)}
{props.messageComponents}
</div>
);
}

export const buildMessages = (messages: Array<string>) => (
Expand Down
60 changes: 57 additions & 3 deletions packages/ui/src/components/Molecules/PageTransition.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import { motion, useReducedMotion } from 'framer-motion';
import React, { PropsWithChildren, useEffect } from 'react';
import React, { PropsWithChildren, ReactNode, useEffect } from 'react';

import { Messages, readMessages } from './Messages';

export function PageTransition({ children }: PropsWithChildren) {
const [messages, setMessages] = React.useState<Array<string>>([]);
const [messageComponents, setMessageComponents] = React.useState<
Array<ReactNode>
>([]);
useEffect(() => {
// Standard messages.
setMessages(readMessages());
// Language message.
const languageMessage = getLanguageMessage(window.location.href);
if (languageMessage) {
setMessageComponents([languageMessage]);
}
}, []);

return useReducedMotion() ? (
<main id="main-content">
<Messages messages={messages} />
<Messages messages={messages} messageComponents={messageComponents} />
{children}
</main>
) : (
Expand All @@ -26,8 +36,52 @@ export function PageTransition({ children }: PropsWithChildren) {
duration: 0.3,
}}
>
<Messages messages={messages} />
<Messages messages={messages} messageComponents={messageComponents} />
{children}
</motion.main>
);
}

function getLanguageMessage(url: string): ReactNode {
const urlObject = new URL(url);
const contentLanguageNotAvailable =
urlObject.searchParams.get('content_language_not_available') === 'true';
if (contentLanguageNotAvailable) {
const requestedLanguage = urlObject.searchParams.get('requested_language');
if (requestedLanguage) {
const translations: {
[language: string]: { message: string; goBack: string };
} = {
en: {
message: 'This page is not available in the requested language.',
goBack: 'Go back',
},
de: {
message:
'Diese Seite ist nicht in der angeforderten Sprache verfügbar.',
goBack: 'Zurück',
},
};
const translation = translations[requestedLanguage];
if (translation) {
return (
<div>
{translation.message}{' '}
<a
href="#"
onClick={() => {
window.history.back();
}}
>
{translation.goBack}
</a>
</div>
);
} else {
console.error(
`Requested language "${requestedLanguage}" not found in messages.`,
);
}
}
}
}

0 comments on commit 30d01e1

Please sign in to comment.