From d0d9de0572a1ea0968f9f56c0a70ca2c093ce2f4 Mon Sep 17 00:00:00 2001
From: Eduardo Morales <eduardo.morales@metadrop.net>
Date: Wed, 19 Feb 2025 13:17:40 +0100
Subject: [PATCH 01/10] Issue #3507728 by eduardo morales alberti: Target
 langcode and source can be different

---
 entity_mesh.services.yml |  8 +++++-
 src/EntityRender.php     | 55 ++++++++++++++++------------------------
 src/Repository.php       | 27 +++++++++++++++++---
 src/Target.php           | 31 +++++++++++++++++++++-
 src/TargetInterface.php  |  4 ++-
 5 files changed, 85 insertions(+), 40 deletions(-)

diff --git a/entity_mesh.services.yml b/entity_mesh.services.yml
index 2b042ef..8b79f02 100644
--- a/entity_mesh.services.yml
+++ b/entity_mesh.services.yml
@@ -5,7 +5,13 @@ services:
     arguments: [ 'entity_mesh' ]
   entity_mesh.repository:
     class: Drupal\entity_mesh\Repository
-    arguments: [ '@database', '@entity_mesh.logger', '@request_stack', '@entity_type.manager', '@entity_field.manager' ]
+    arguments:
+      - '@database'
+      - '@entity_mesh.logger'
+      - '@request_stack'
+      - '@entity_type.manager'
+      - '@entity_field.manager'
+      - '@config.factory'
   entity_mesh.menu:
     class: Drupal\entity_mesh\Menu
     arguments: ['@entity_mesh.repository', '@entity_type.manager', '@language_manager', '@config.factory']
diff --git a/src/EntityRender.php b/src/EntityRender.php
index 2b23132..08206f1 100644
--- a/src/EntityRender.php
+++ b/src/EntityRender.php
@@ -57,7 +57,7 @@ class EntityRender extends Entity {
    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
-   *   The language manager.
+   *   The config factory.
    * @param \Drupal\Core\Render\RendererInterface $renderer
    *   The renderer manager.
    * @param \Drupal\Core\Session\AccountSwitcherInterface $account_switcher
@@ -194,6 +194,7 @@ class EntityRender extends Entity {
     $view_builder = $this->entityTypeManager->getViewBuilder($entity->getEntityTypeId());
 
     $pre_render = $view_builder->view($entity, $view_mode, $langcode);
+    // @todo Review render plain is deprecated.
     $render_output = $this->renderer->renderPlain($pre_render);
 
     // Switches back to the current language:
@@ -243,7 +244,7 @@ class EntityRender extends Entity {
     $target->setCategory('link');
 
     if ($target->getLinkType() == 'internal' && $this->ifProcessInternalTarget()) {
-      $this->processInternalHref($target, $source->getSourceEntityLangcode() ?? '');
+      $this->processInternalHref($target, $target->getEntityLangcode() ?? '');
 
       // Check that the target is not the source to avoid circular references.
       if ($source->getSourceEntityType() == $target->getEntityType()
@@ -263,34 +264,36 @@ class EntityRender extends Entity {
    *
    * @param \Drupal\entity_mesh\TargetInterface $target
    *   The target.
-   * @param string $langcode
-   *   The language code.
    */
-  protected function processInternalHref(TargetInterface $target, string $langcode) {
+  protected function processInternalHref(TargetInterface $target) {
     $found_data = FALSE;
     // Remove the langcode to find the alias.
     // @todo take into account other languages also to support cross-language paths.
     // @todo this is the default URL creation, change with the real one.
-    $prefix = $this->prefixes[$langcode] ?? $langcode;
+    $prefix = '';
+    if ($langcode = $target->getEntityLangcode()) {
+      $prefix = $this->prefixes[$langcode] ?? $langcode;
+    }
+
     $alias = str_replace('/' . $prefix, '', $target->getPath() ?? '');
 
-    $found_data = $this->setDataIfRedirection($alias, $langcode, $target);
+    $found_data = $this->setDataIfRedirection($alias, $target);
 
     // Get the info from alias.
     // This method is quicker than using the router service,
     // so firstly apply this system.
     if (!$found_data) {
-      $found_data = $this->setDataTargetFromAliasIfExists($alias, $langcode, $target);
+      $found_data = $this->setDataTargetFromAliasIfExists($alias, $target);
     }
 
     // If this method not found the data, use the router service.
     if (!$found_data) {
-      $this->setDataTargetFromRoute($langcode, $target);
+      $this->setDataTargetFromRoute($target);
     }
 
     // If the target is set as link broken maybe is because is a file url.
     if ($target->getSubcategory() === 'broken-link') {
-      $this->setDataTargetIfFileUrl($langcode, $target);
+      $this->setDataTargetIfFileUrl($target);
     }
 
     $this->setBundleInTarget($target);
@@ -328,15 +331,14 @@ class EntityRender extends Entity {
    *
    * @param string $alias
    *   The alias.
-   * @param string $langcode
-   *   The language code.
    * @param TargetInterface $target
    *   The target.
    *
    * @return bool
    *   If is a redirection.
    */
-  protected function setDataIfRedirection(string &$alias, string $langcode, TargetInterface $target): bool {
+  protected function setDataIfRedirection(string &$alias, TargetInterface $target): bool {
+    $langcode = $target->getEntityLangcode();
     // Check if is a redirected link.
     $uri = $this->entityMeshRepository->ifRedirectionForPath($alias, $langcode);
     if ($uri === NULL) {
@@ -375,7 +377,7 @@ class EntityRender extends Entity {
       $uri = $possible_alias->path;
     }
 
-    return $this->processInternalPaths($uri, $langcode, $target);
+    return $this->processInternalPaths($uri, $target);
   }
 
   /**
@@ -383,21 +385,20 @@ class EntityRender extends Entity {
    *
    * @param string $alias
    *   Alias.
-   * @param string $langcode
-   *   Langcode.
    * @param TargetInterface $target
    *   Target.
    *
    * @return bool
    *   If this method found the data.
    */
-  protected function setDataTargetFromAliasIfExists(string $alias, string $langcode, TargetInterface $target): bool {
+  protected function setDataTargetFromAliasIfExists(string $alias, TargetInterface $target): bool {
     // Get the info from alias.
+    $langcode = $target->getEntityLangcode();
     $record = $this->getPathFromAliasTables($alias, $langcode);
     if (!$record) {
       return FALSE;
     }
-    return $this->processInternalPaths($record->path, $langcode, $target);
+    return $this->processInternalPaths($record->path, $target);
   }
 
   /**
@@ -405,15 +406,13 @@ class EntityRender extends Entity {
    *
    * @param string $path
    *   The path.
-   * @param string $langcode
-   *   The language code.
    * @param TargetInterface $target
    *   The target.
    *
    * @return bool
    *   If the path is processed.
    */
-  protected function processInternalPaths(string $path, string $langcode, TargetInterface $target) {
+  protected function processInternalPaths(string $path, TargetInterface $target) {
     $path = explode('/', ltrim($path, '/'));
     if (count($path) < 2) {
       return FALSE;
@@ -425,25 +424,21 @@ class EntityRender extends Entity {
     if (isset($path[2]) && is_numeric($path[2])) {
       $target->setEntityType($path[0]);
       $target->setEntityId((string) $path[2]);
-      $target->setEntityLangcode($langcode);
       return TRUE;
     }
 
     $target->setEntityType($path[0]);
     $target->setEntityId((string) ($path[1] ?? ''));
-    $target->setEntityLangcode($langcode);
     return TRUE;
   }
 
   /**
    * Set data about the target from route.
    *
-   * @param string $langcode
-   *   The language code.
    * @param TargetInterface $target
    *   The target.
    */
-  protected function setDataTargetFromRoute($langcode, $target) {
+  protected function setDataTargetFromRoute($target) {
     if (empty($target->getPath())) {
       return;
     }
@@ -461,7 +456,6 @@ class EntityRender extends Entity {
     if (isset($route_match['view_id'])) {
       $target->setEntityType('view');
       $target->setEntityId($route_match['view_id'] . '.' . $route_match['display_id']);
-      $target->setEntityLangcode($langcode);
       return;
     }
 
@@ -475,19 +469,16 @@ class EntityRender extends Entity {
       }
       $target->setEntityType($entity);
       $target->setEntityId((string) $entity_id);
-      $target->setEntityLangcode($langcode);
     }
   }
 
   /**
    * Set data target if file url.
    *
-   * @param string $langcode
-   *   The language code.
    * @param TargetInterface $target
    *   The target.
    */
-  protected function setDataTargetIfFileUrl($langcode, $target) {
+  protected function setDataTargetIfFileUrl($target) {
     // Check if the path is a file.
     $path = $this->entityMeshRepository->getPathFromFileUrl($target->getPath() ?? '');
     if (!$path) {
@@ -509,14 +500,12 @@ class EntityRender extends Entity {
     if (!$media) {
       $target->setEntityType($file->getEntityTypeId());
       $target->setEntityId((string) $file->id());
-      $target->setEntityLangcode($langcode);
       return;
     }
 
     $target->setEntityType($media->getEntityTypeId());
     $target->setEntityBundle($media->bundle());
     $target->setEntityId((string) $media->id());
-    $target->setEntityLangcode($langcode);
   }
 
   /**
diff --git a/src/Repository.php b/src/Repository.php
index 6f78a51..677f40b 100644
--- a/src/Repository.php
+++ b/src/Repository.php
@@ -3,6 +3,7 @@
 namespace Drupal\entity_mesh;
 
 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
+use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityInterface;
@@ -14,7 +15,6 @@ use Drupal\redirect\Exception\RedirectLoopException;
 use Psr\Log\LoggerInterface;
 use Symfony\Component\HttpFoundation\RequestStack;
 
-
 /**
  * Service to perform database operations.
  */
@@ -55,6 +55,13 @@ class Repository implements RepositoryInterface {
    */
   protected $entityFieldManager;
 
+  /**
+   * Url language prefixes.
+   *
+   * @var array
+   */
+  protected array $prefixes;
+
   /**
    * Constructs a new Repository object.
    *
@@ -68,13 +75,23 @@ class Repository implements RepositoryInterface {
    *   The entity type manager.
    * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
    *   The entity field manager.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   Config factory.
    */
-  public function __construct(Connection $database, LoggerInterface $logger, RequestStack $request_stack, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager) {
+  public function __construct(
+    Connection $database,
+    LoggerInterface $logger,
+    RequestStack $request_stack,
+    EntityTypeManagerInterface $entity_type_manager,
+    EntityFieldManagerInterface $entity_field_manager,
+    ConfigFactoryInterface $config_factory,
+  ) {
     $this->database = $database;
     $this->logger = $logger;
     $this->requestStack = $request_stack;
     $this->entityTypeManager = $entity_type_manager;
     $this->entityFieldManager = $entity_field_manager;
+    $this->prefixes = $config_factory->get('language.negotiation')->get('url.prefixes');
   }
 
   /**
@@ -311,7 +328,8 @@ class Repository implements RepositoryInterface {
     /** @var \Symfony\Component\HttpFoundation\Request $current_request */
     $current_request = $this->requestStack->getCurrentRequest();
     $domain = $current_request->getHost();
-    return Target::create($domain);
+    $prefixes = $this->prefixes;
+    return Target::create($domain, $prefixes);
   }
 
   /**
@@ -398,7 +416,8 @@ class Repository implements RepositoryInterface {
     try {
       /** @var  \Drupal\redirect\Entity\Redirect $redirect_object */
       $redirect_object = $redirect_repository->findMatchingRedirect($path, [], $langcode);
-    } catch (RedirectLoopException $e) {
+    }
+    catch (RedirectLoopException $e) {
       $this->logger->error($e->getMessage());
       return NULL;
     }
diff --git a/src/Target.php b/src/Target.php
index 6208e42..de33931 100644
--- a/src/Target.php
+++ b/src/Target.php
@@ -102,12 +102,20 @@ class Target implements TargetInterface {
    */
   protected $title;
 
+  /**
+   * Prefixes.
+   *
+   * @var array
+   */
+  protected array $prefixes;
+
   /**
    * {@inheritdoc}
    */
-  public static function create(string $current_domain): Target {
+  public static function create(string $current_domain, array $prefixes): Target {
     $target = new self();
     $target->currentDomain = $current_domain;
+    $target->prefixes = $prefixes;
     return $target;
   }
 
@@ -172,6 +180,7 @@ class Target implements TargetInterface {
         $path = $this->sanitizeUrl($path);
       }
       $this->setPath($path);
+      $this->getTargetLangcodeFromPath($path);
     }
   }
 
@@ -335,6 +344,26 @@ class Target implements TargetInterface {
     }
   }
 
+  /**
+   * Extract langcode from path.
+   *
+   * @param mixed $path
+   *   Patch to review.
+   *
+   * @return string|null
+   *   Target langcode.
+   */
+  public function getTargetLangcodeFromPath(mixed $path) {
+    $path = '/' . ltrim($path, '/');
+    foreach ($this->prefixes as $langcode => $prefix) {
+      if ($prefix && str_starts_with($path, '/' . $prefix . '/')) {
+        $this->setEntityLangcode($langcode);
+      }
+    }
+
+    return $this->targetEntityLangcode;
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/src/TargetInterface.php b/src/TargetInterface.php
index eec26b3..562d8d2 100644
--- a/src/TargetInterface.php
+++ b/src/TargetInterface.php
@@ -216,10 +216,12 @@ interface TargetInterface {
    *
    * @param string $current_domain
    *   The current domain.
+   * @param array $prefixes
+   *   Prefixes.
    *
    * @return \Drupal\entity_mesh\TargetInterface
    *   The target object.
    */
-  public static function create(string $current_domain): TargetInterface;
+  public static function create(string $current_domain, array $prefixes): TargetInterface;
 
 }
-- 
GitLab


From 1a8ce35c7e8a0e80f117b39b8239b16a9d78595a Mon Sep 17 00:00:00 2001
From: Eduardo Morales <eduardo.morales@metadrop.net>
Date: Wed, 19 Feb 2025 13:26:04 +0100
Subject: [PATCH 02/10] Issue #3507728 by eduardo morales alberti: Target
 langcode and source can be different

---
 src/EntityRender.php    | 10 +---------
 src/Target.php          | 16 ++++++++++++++++
 src/TargetInterface.php |  8 ++++++++
 3 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/src/EntityRender.php b/src/EntityRender.php
index 08206f1..b76c9fc 100644
--- a/src/EntityRender.php
+++ b/src/EntityRender.php
@@ -266,16 +266,8 @@ class EntityRender extends Entity {
    *   The target.
    */
   protected function processInternalHref(TargetInterface $target) {
-    $found_data = FALSE;
-    // Remove the langcode to find the alias.
-    // @todo take into account other languages also to support cross-language paths.
-    // @todo this is the default URL creation, change with the real one.
-    $prefix = '';
-    if ($langcode = $target->getEntityLangcode()) {
-      $prefix = $this->prefixes[$langcode] ?? $langcode;
-    }
 
-    $alias = str_replace('/' . $prefix, '', $target->getPath() ?? '');
+    $alias = $target->getAlias();
 
     $found_data = $this->setDataIfRedirection($alias, $target);
 
diff --git a/src/Target.php b/src/Target.php
index de33931..d2ab9f3 100644
--- a/src/Target.php
+++ b/src/Target.php
@@ -264,6 +264,22 @@ class Target implements TargetInterface {
     $this->targetEntityLangcode = $entity_langcode;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getAlias(): ?string {
+    // Remove the langcode to find the alias.
+    // @todo take into account other languages also to support cross-language paths.
+    // @todo this is the default URL creation, change with the real one.
+    $prefix = '';
+    if ($langcode = $this->getEntityLangcode()) {
+      $prefix = $this->prefixes[$langcode] ?? $langcode;
+    }
+
+    $alias = str_replace('/' . $prefix, '', $this->getPath() ?? '');
+    return $alias;
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/src/TargetInterface.php b/src/TargetInterface.php
index 562d8d2..f0d04d4 100644
--- a/src/TargetInterface.php
+++ b/src/TargetInterface.php
@@ -65,6 +65,14 @@ interface TargetInterface {
    */
   public function processHrefAndSetComponents(string $href);
 
+  /**
+   * Extract alias from target path.
+   *
+   * @return string|null
+   *   Alias from path.
+   */
+  public function getAlias(): ?string;
+
   /**
    * Get the link type.
    *
-- 
GitLab


From 51f70c1aefcbbb3526fadf793c8ab5a914ca4e65 Mon Sep 17 00:00:00 2001
From: Eduardo Morales <eduardo.morales@metadrop.net>
Date: Thu, 20 Feb 2025 11:00:21 +0100
Subject: [PATCH 03/10] Issue #3507728 by eduardo morales alberti: Target
 langcode and source can be different

---
 src/EntityRender.php | 9 ++++++---
 src/Target.php       | 4 ++--
 2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/src/EntityRender.php b/src/EntityRender.php
index b76c9fc..9e250a5 100644
--- a/src/EntityRender.php
+++ b/src/EntityRender.php
@@ -244,7 +244,7 @@ class EntityRender extends Entity {
     $target->setCategory('link');
 
     if ($target->getLinkType() == 'internal' && $this->ifProcessInternalTarget()) {
-      $this->processInternalHref($target, $target->getEntityLangcode() ?? '');
+      $this->processInternalHref($target);
 
       // Check that the target is not the source to avoid circular references.
       if ($source->getSourceEntityType() == $target->getEntityType()
@@ -511,14 +511,17 @@ class EntityRender extends Entity {
    * @return mixed|null
    *   Return the path.
    */
-  protected function getPathFromAliasTables(string $alias, string $langcode) {
+  protected function getPathFromAliasTables(string $alias, string $langcode = NULL) {
     $query = $this->entityMeshRepository->getDatabaseService()->select('path_alias', 'pa');
     $query->fields('pa', ['path']);
     $or = $query->orConditionGroup()
       ->condition('alias', $alias)
       ->condition('path', $alias);
     $query->condition($or);
-    $query->condition('langcode', $langcode);
+    if ($langcode !== NULL) {
+      $query->condition('langcode', $langcode);
+    }
+
     $result = $query->execute();
     if (!$result instanceof StatementInterface) {
       return NULL;
diff --git a/src/Target.php b/src/Target.php
index d2ab9f3..1d94950 100644
--- a/src/Target.php
+++ b/src/Target.php
@@ -271,12 +271,12 @@ class Target implements TargetInterface {
     // Remove the langcode to find the alias.
     // @todo take into account other languages also to support cross-language paths.
     // @todo this is the default URL creation, change with the real one.
-    $prefix = '';
+    $alias = $this->getPath();
     if ($langcode = $this->getEntityLangcode()) {
       $prefix = $this->prefixes[$langcode] ?? $langcode;
+      $alias = str_replace('/' . $prefix, '', $this->getPath() ?? '');
     }
 
-    $alias = str_replace('/' . $prefix, '', $this->getPath() ?? '');
     return $alias;
   }
 
-- 
GitLab


From 59d4417b1ad64a7fa2bdb37d5f3a4cf5d0a40ff3 Mon Sep 17 00:00:00 2001
From: lpeidro <luis.ruiz@metadrop.net>
Date: Thu, 20 Feb 2025 12:40:07 +0100
Subject: [PATCH 04/10] Issue #3507728: Move logic about lang prefix from
 Target object to service Repostiroty. It is needed in other contexts

---
 src/Repository.php          | 65 +++++++++++++++++++++++++++++++++++--
 src/RepositoryInterface.php | 33 +++++++++++++++++++
 src/Target.php              | 46 +-------------------------
 src/TargetInterface.php     | 12 +------
 4 files changed, 98 insertions(+), 58 deletions(-)

diff --git a/src/Repository.php b/src/Repository.php
index 677f40b..8e60105 100644
--- a/src/Repository.php
+++ b/src/Repository.php
@@ -328,8 +328,7 @@ class Repository implements RepositoryInterface {
     /** @var \Symfony\Component\HttpFoundation\Request $current_request */
     $current_request = $this->requestStack->getCurrentRequest();
     $domain = $current_request->getHost();
-    $prefixes = $this->prefixes;
-    return Target::create($domain, $prefixes);
+    return Target::create($domain);
   }
 
   /**
@@ -461,4 +460,66 @@ class Repository implements RepositoryInterface {
     return $uri;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getPathWithoutLangPrefix($path) {
+    $prefix = $this->getLangPrefixFromPath($path);
+    if (empty($prefix)) {
+      return $path;
+    }
+    $path = ltrim($path, '/');
+    return str_replace($prefix, '', $path ?? '');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLangcodeFromPath($path): ?string {
+    $result = NULL;
+    if ($langcode_and_prefix = $this->getLangcodeAndPrefixFromPath($path)) {
+      $result = $langcode_and_prefix['langcode'] ?? NULL;
+    }
+    return $result;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLangPrefixFromPath($path): ?string {
+    $result = NULL;
+    if ($langcode_and_prefix = $this->getLangcodeAndPrefixFromPath($path)) {
+      $result = $langcode_and_prefix['prefix'] ?? NULL;
+    }
+    return $result;
+  }
+
+  /**
+   * Get the langcode and prefix from the path.
+   *
+   * @param string $path
+   *   The path.
+   *
+   * @return array|null
+   *   The langcode and prefix or NULL.
+   */
+  protected function getLangcodeAndPrefixFromPath($path): ?array {
+    // If the prefixes ares empty, it is not a multilanguage site, so
+    // there are not langcode to return.
+    if (empty($this->prefixes)) {
+      return NULL;
+    }
+    $path = '/' . ltrim($path, '/');
+    foreach ($this->prefixes as $langcode => $prefix) {
+      if ($prefix && str_starts_with($path, '/' . $prefix . '/')) {
+        return [
+          'langcode' => $langcode,
+          'prefix' => $prefix,
+        ];
+      }
+    }
+
+    return NULL;
+  }
+
 }
diff --git a/src/RepositoryInterface.php b/src/RepositoryInterface.php
index 5d56a2d..b2d5757 100644
--- a/src/RepositoryInterface.php
+++ b/src/RepositoryInterface.php
@@ -143,4 +143,37 @@ interface RepositoryInterface {
    */
   public function ifRedirectionForPath(string $path, string $langcode, int $count = 0);
 
+  /**
+   * Get the path without the lang prefix.
+   *
+   * @param string $path
+   *   The path.
+   *
+   * @return string
+   *   The path without the lang prefix.
+   */
+  public function getPathWithoutLangPrefix($path);
+
+  /**
+   * Get the langcode from the path.
+   *
+   * @param string $path
+   *   The path.
+   *
+   * @return string|null
+   *   The langcode or NULL.
+   */
+  public function getLangcodeFromPath($path): ?string;
+
+  /**
+   * Get the lang prefix from the path.
+   *
+   * @param string $path
+   *   The path.
+   *
+   * @return string|null
+   *   The langcode or NULL.
+   */
+  public function getLangPrefixFromPath($path): ?string;
+
 }
diff --git a/src/Target.php b/src/Target.php
index 1d94950..d7082a6 100644
--- a/src/Target.php
+++ b/src/Target.php
@@ -102,20 +102,12 @@ class Target implements TargetInterface {
    */
   protected $title;
 
-  /**
-   * Prefixes.
-   *
-   * @var array
-   */
-  protected array $prefixes;
-
   /**
    * {@inheritdoc}
    */
-  public static function create(string $current_domain, array $prefixes): Target {
+  public static function create(string $current_domain): Target {
     $target = new self();
     $target->currentDomain = $current_domain;
-    $target->prefixes = $prefixes;
     return $target;
   }
 
@@ -264,22 +256,6 @@ class Target implements TargetInterface {
     $this->targetEntityLangcode = $entity_langcode;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getAlias(): ?string {
-    // Remove the langcode to find the alias.
-    // @todo take into account other languages also to support cross-language paths.
-    // @todo this is the default URL creation, change with the real one.
-    $alias = $this->getPath();
-    if ($langcode = $this->getEntityLangcode()) {
-      $prefix = $this->prefixes[$langcode] ?? $langcode;
-      $alias = str_replace('/' . $prefix, '', $this->getPath() ?? '');
-    }
-
-    return $alias;
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -360,26 +336,6 @@ class Target implements TargetInterface {
     }
   }
 
-  /**
-   * Extract langcode from path.
-   *
-   * @param mixed $path
-   *   Patch to review.
-   *
-   * @return string|null
-   *   Target langcode.
-   */
-  public function getTargetLangcodeFromPath(mixed $path) {
-    $path = '/' . ltrim($path, '/');
-    foreach ($this->prefixes as $langcode => $prefix) {
-      if ($prefix && str_starts_with($path, '/' . $prefix . '/')) {
-        $this->setEntityLangcode($langcode);
-      }
-    }
-
-    return $this->targetEntityLangcode;
-  }
-
   /**
    * {@inheritdoc}
    */
diff --git a/src/TargetInterface.php b/src/TargetInterface.php
index f0d04d4..eec26b3 100644
--- a/src/TargetInterface.php
+++ b/src/TargetInterface.php
@@ -65,14 +65,6 @@ interface TargetInterface {
    */
   public function processHrefAndSetComponents(string $href);
 
-  /**
-   * Extract alias from target path.
-   *
-   * @return string|null
-   *   Alias from path.
-   */
-  public function getAlias(): ?string;
-
   /**
    * Get the link type.
    *
@@ -224,12 +216,10 @@ interface TargetInterface {
    *
    * @param string $current_domain
    *   The current domain.
-   * @param array $prefixes
-   *   Prefixes.
    *
    * @return \Drupal\entity_mesh\TargetInterface
    *   The target object.
    */
-  public static function create(string $current_domain, array $prefixes): TargetInterface;
+  public static function create(string $current_domain): TargetInterface;
 
 }
-- 
GitLab


From 7f9ac2f9986daf4180db0c6864f0d04a9cdae1da Mon Sep 17 00:00:00 2001
From: lpeidro <luis.ruiz@metadrop.net>
Date: Thu, 20 Feb 2025 12:56:42 +0100
Subject: [PATCH 05/10] Issue #3507728: Prefix languages are manage by servuce
 repository, not need in EntityRender service

---
 src/EntityRender.php | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/src/EntityRender.php b/src/EntityRender.php
index 9e250a5..70f27c1 100644
--- a/src/EntityRender.php
+++ b/src/EntityRender.php
@@ -40,13 +40,6 @@ class EntityRender extends Entity {
    */
   protected LanguageNegotiatorSwitcher $languageNegotiatorSwitcher;
 
-  /**
-   * Url language prefixes.
-   *
-   * @var array
-   */
-  protected array $prefixes;
-
   /**
    * Constructs a Menu object.
    *
@@ -79,7 +72,6 @@ class EntityRender extends Entity {
     $this->accountSwitcher = $account_switcher;
     $this->type = 'entity_render';
     $this->languageNegotiatorSwitcher = $language_negotiator_switcher;
-    $this->prefixes = $config_factory->get('language.negotiation')->get('url.prefixes');
   }
 
   /**
-- 
GitLab


From 2c5509a54b103aec37aa80b9586afd38a24178d7 Mon Sep 17 00:00:00 2001
From: lpeidro <luis.ruiz@metadrop.net>
Date: Thu, 20 Feb 2025 12:59:00 +0100
Subject: [PATCH 06/10] Issue #3507728: Remove not needed argument in method

---
 src/Target.php | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/Target.php b/src/Target.php
index d7082a6..6208e42 100644
--- a/src/Target.php
+++ b/src/Target.php
@@ -172,7 +172,6 @@ class Target implements TargetInterface {
         $path = $this->sanitizeUrl($path);
       }
       $this->setPath($path);
-      $this->getTargetLangcodeFromPath($path);
     }
   }
 
-- 
GitLab


From 8ed8a2810ae5ee685852b4cc66bbc19c8a05fcd5 Mon Sep 17 00:00:00 2001
From: lpeidro <luis.ruiz@metadrop.net>
Date: Thu, 20 Feb 2025 13:46:21 +0100
Subject: [PATCH 07/10] Issue #3507728: Add possibility to check alias without
 langcode.

---
 src/EntityRender.php | 16 ++++++----------
 1 file changed, 6 insertions(+), 10 deletions(-)

diff --git a/src/EntityRender.php b/src/EntityRender.php
index 70f27c1..77b17c0 100644
--- a/src/EntityRender.php
+++ b/src/EntityRender.php
@@ -259,7 +259,8 @@ class EntityRender extends Entity {
    */
   protected function processInternalHref(TargetInterface $target) {
 
-    $alias = $target->getAlias();
+    $alias = $this->entityMeshRepository->getPathWithoutLangPrefix($target->getPath());
+    $target->setEntityLangcode($this->entityMeshRepository->getLangcodeFromPath($target->getPath()));
 
     $found_data = $this->setDataIfRedirection($alias, $target);
 
@@ -347,9 +348,7 @@ class EntityRender extends Entity {
       return TRUE;
     }
 
-    if (str_starts_with($uri, "/$langcode")) {
-      $uri = substr($uri, strlen("/$langcode"));
-    }
+    $uri = $this->entityMeshRepository->getPathWithoutLangPrefix($uri);
 
     if ($type === '') {
       $alias = $uri;
@@ -377,7 +376,7 @@ class EntityRender extends Entity {
    */
   protected function setDataTargetFromAliasIfExists(string $alias, TargetInterface $target): bool {
     // Get the info from alias.
-    $langcode = $target->getEntityLangcode();
+    $langcode = $target->getEntityLangcode() ?? '';
     $record = $this->getPathFromAliasTables($alias, $langcode);
     if (!$record) {
       return FALSE;
@@ -503,17 +502,14 @@ class EntityRender extends Entity {
    * @return mixed|null
    *   Return the path.
    */
-  protected function getPathFromAliasTables(string $alias, string $langcode = NULL) {
+  protected function getPathFromAliasTables(string $alias, string $langcode) {
     $query = $this->entityMeshRepository->getDatabaseService()->select('path_alias', 'pa');
     $query->fields('pa', ['path']);
     $or = $query->orConditionGroup()
       ->condition('alias', $alias)
       ->condition('path', $alias);
     $query->condition($or);
-    if ($langcode !== NULL) {
-      $query->condition('langcode', $langcode);
-    }
-
+    $query->condition('langcode', $langcode);
     $result = $query->execute();
     if (!$result instanceof StatementInterface) {
       return NULL;
-- 
GitLab


From 62bb0323d81fe93411d0d88518a8f81acfa3b458 Mon Sep 17 00:00:00 2001
From: lpeidro <luis.ruiz@metadrop.net>
Date: Thu, 20 Feb 2025 14:16:18 +0100
Subject: [PATCH 08/10] Issue #3507728: Fix rebase

---
 src/EntityRender.php | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/EntityRender.php b/src/EntityRender.php
index 77b17c0..5c5ca07 100644
--- a/src/EntityRender.php
+++ b/src/EntityRender.php
@@ -509,7 +509,9 @@ class EntityRender extends Entity {
       ->condition('alias', $alias)
       ->condition('path', $alias);
     $query->condition($or);
-    $query->condition('langcode', $langcode);
+    if (!empty($langcode)) {
+      $query->condition('langcode', $langcode);
+    }
     $result = $query->execute();
     if (!$result instanceof StatementInterface) {
       return NULL;
-- 
GitLab


From c26f3e0b68837c0e8ec1a3397fa2d935b364d191 Mon Sep 17 00:00:00 2001
From: lpeidro <luis.ruiz@metadrop.net>
Date: Thu, 20 Feb 2025 15:02:17 +0100
Subject: [PATCH 09/10] Issue #3507728: Ensure that langcode al least is an
 empty string

---
 src/EntityRender.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/EntityRender.php b/src/EntityRender.php
index 5c5ca07..28e9ef5 100644
--- a/src/EntityRender.php
+++ b/src/EntityRender.php
@@ -323,7 +323,7 @@ class EntityRender extends Entity {
    *   If is a redirection.
    */
   protected function setDataIfRedirection(string &$alias, TargetInterface $target): bool {
-    $langcode = $target->getEntityLangcode();
+    $langcode = $target->getEntityLangcode() ?? '';
     // Check if is a redirected link.
     $uri = $this->entityMeshRepository->ifRedirectionForPath($alias, $langcode);
     if ($uri === NULL) {
-- 
GitLab


From 98a35428a18bfae19ce7a6193bfd3c8a5a96296c Mon Sep 17 00:00:00 2001
From: Eduardo Morales <eduardo.morales@metadrop.net>
Date: Thu, 20 Feb 2025 15:08:40 +0100
Subject: [PATCH 10/10] Issue #3507728 by eduardo morales alberti: Target
 langcode and source can be different

---
 entity_mesh.services.yml | 10 +++++++++-
 src/EntityRender.php     | 28 ++++++++++++++++++++++------
 2 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/entity_mesh.services.yml b/entity_mesh.services.yml
index 8b79f02..b776574 100644
--- a/entity_mesh.services.yml
+++ b/entity_mesh.services.yml
@@ -17,7 +17,15 @@ services:
     arguments: ['@entity_mesh.repository', '@entity_type.manager', '@language_manager', '@config.factory']
   entity_mesh.entity_render:
     class: Drupal\entity_mesh\EntityRender
-    arguments: ['@entity_mesh.repository', '@entity_type.manager', '@language_manager', '@config.factory', '@renderer', '@account_switcher', '@entity_mesh.language_negotiator_switcher']
+    arguments:
+    - '@entity_mesh.repository'
+    - '@entity_type.manager'
+    - '@language_manager'
+    - '@config.factory'
+    - '@renderer'
+    - '@account_switcher'
+    - '@entity_mesh.language_negotiator_switcher'
+    - '@module_handler'
   entity_mesh.language_negotiator_switcher:
     class: Drupal\entity_mesh\Language\LanguageNegotiatorSwitcher
     arguments: ['@language_manager', '@module_handler', '@string_translation', '@entity_mesh.static_language_negotiator']
diff --git a/src/EntityRender.php b/src/EntityRender.php
index 28e9ef5..8c78439 100644
--- a/src/EntityRender.php
+++ b/src/EntityRender.php
@@ -7,6 +7,7 @@ use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Database\StatementInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Session\AccountSwitcherInterface;
@@ -40,6 +41,14 @@ class EntityRender extends Entity {
    */
   protected LanguageNegotiatorSwitcher $languageNegotiatorSwitcher;
 
+
+  /**
+   * Module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected ModuleHandlerInterface $moduleHandler;
+
   /**
    * Constructs a Menu object.
    *
@@ -57,6 +66,8 @@ class EntityRender extends Entity {
    *   The AccountSwitcher manager.
    * @param \Drupal\entity_mesh\Language\LanguageNegotiatorSwitcher $language_negotiator_switcher
    *   The language negotiator switcher service.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   Module handler.
    */
   public function __construct(
     RepositoryInterface $entity_mesh_repository,
@@ -66,12 +77,14 @@ class EntityRender extends Entity {
     RendererInterface $renderer,
     AccountSwitcherInterface $account_switcher,
     LanguageNegotiatorSwitcher $language_negotiator_switcher,
+    ModuleHandlerInterface $module_handler,
   ) {
-    parent::__construct($entity_mesh_repository, $entity_type_manager, $language_manager, $config_factory);
+    parent::__construct($entity_mesh_repository, $entity_type_manager, $language_manager, $config_factory, $module_handler);
     $this->renderer = $renderer;
     $this->accountSwitcher = $account_switcher;
     $this->type = 'entity_render';
     $this->languageNegotiatorSwitcher = $language_negotiator_switcher;
+    $this->moduleHandler = $module_handler;
   }
 
   /**
@@ -376,12 +389,15 @@ class EntityRender extends Entity {
    */
   protected function setDataTargetFromAliasIfExists(string $alias, TargetInterface $target): bool {
     // Get the info from alias.
-    $langcode = $target->getEntityLangcode() ?? '';
-    $record = $this->getPathFromAliasTables($alias, $langcode);
-    if (!$record) {
-      return FALSE;
+    $langcode = $target->getEntityLangcode();
+    if ($this->moduleHandler->moduleExists('path_alias')) {
+      $record = $this->getPathFromAliasTables($alias, $langcode);
+      if ($record) {
+        return $this->processInternalPaths($record->path, $target);
+      }
     }
-    return $this->processInternalPaths($record->path, $target);
+    return $this->processInternalPaths($alias, $target);
+
   }
 
   /**
-- 
GitLab