Url::fromUri() fails on internal routes with alias in a different language

Created on 22 September 2021, over 2 years ago
Updated 19 March 2024, 3 months ago

Problem/Motivation

When you are using URL aliases (module path_alias) on nodes and then try to build a URL object via Drupal\Core\Url::fromUri('internal:/language-prefix/your-alias') it will not create the correct URL object if the alias belongs to a language that is different from the language of the current page request.

There are several components involved, which is making it hard for me to tell which component is having the issue. It is more like the interaction of the components seems to be problematic while each part on its own is working as intended.

The call stack will look something like this:

Drupal\Core\Url::fromUri()
Drupal\Core\Url::fromInternalUri()
Drupal\Core\Path\PathValidator::getUrlIfValidWithoutAccessCheck()
Drupal\Core\Path\PathValidator::getUrl()
Drupal\Core\Path\PathValidator::getPathAttributes()
Drupal\Core\PathProcessor\PathProcessorManager::processInbound()

There are two path processors that may be relevant in this scenario.

The processor method Drupal\language\HttpKernal\PathProcessorLanguage::processInbound() is removing the language prefix from the given path. It has to do it so that following path processors get the original path without the language prefix, however, this is the point where the intended language for the alias gets lost.

The processor method Drupal\path_alias\PathProcessor\AliasPathProcessor::processInbound() should actually find the correct route by calling Drupal\path_alias\AliasManager::getPathByAlias(), but it does not provide a language in which the alias should be searched for and the AliasManager will therefore fall back to the language of the given request.

The URL alias will not be found. The URL object will be unrouted.

As a side note: The method Url::fromUri() does have a second argument $options in which we can provide a language, however, that language is not passed to the path processors. So even if we knew the language of the alias in advance (which we don't in this scenario), the alias would still not be recognised correctly. The resulting URL object would have the correct language set, but it would still be unrouted.

Steps to reproduce

Have a Drupal system with languages English (default, no path prefix) and German (path prefix "de").

Have a node with translations in English and German. English URL alias: "foo/bar". German URL alias: "de-foo/de-bar".

The URLs would be /foo/bar and /de/de-foo/de-bar.

Load any page in English (without language path prefix) and during the request execute the following code:

\Drupal\Code\Url::fromUri('internal:/de/de-foo/de-bar');

The resulting URL object is marked as unrouted.

As described before, the following code will not produce the correct result either:

\Drupal\Code\Url::fromUri('internal:/de/de-foo/de-bar', ['language' => \Drupal::languageManager->getLanguage('de')]);

Now load any page in German (language path prefix "de") and execute the same code. The resulting URL object will be correct.

Proposed resolution

I guess the best way to approach this issue would be to update Drupal\Core\Path\PathValidator (getUrl() or getPathAttributes()). I am not sure if all languages should be used to find the correct alias record. The language is already encoded in the given path "/de/". However, there may also be fallback languages involved.

To be honest, I am not even sure if this is an actual bug or if the method Url::fromUri() should just not be used like that. I know at least one module (trailing_slash) that is using it in this way and it fails to perform its actions properly because of that.

Another way to deal with the issue would probably be to let the method Url::fromUri() pass the given language (from its options) to the path validator (and to its path processors) so that the AliasPathProcessor knows in which language the alias should be searched for. The problem of having to know the language in advance would remain, but I can see that this is just the overall limit of this function.

Please let me know your thoughts on this. Should the method Url::fromUri() just not be used like that? Or is it a valid use-case and therefore a bug?

🐛 Bug report
Status

Active

Version

11.0 🔥

Component
Path 

Last updated 6 days ago

  • Maintained by
  • 🇬🇧United Kingdom @catch
Created by

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

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • 🇫🇮Finland jhuhta

    Confirming the problem.
    I have a list of path aliases like these:
    /en/english-alias
    /fi/finnish-alias
    /sv/swedish-alias

    And I'd like to create Url objects of them, removing those that point to other translations than the one of active language. But Drupal don't seem to consider those ones with non-active language prefix routable urls, even though the creation function has been explicitly provided with the correct language option.

    Following the code with debugger, it seems getRouteCollectionCacheId() doesn't use the provided language anymore, but uses the current language instead. Which leads to null result.

Production build 0.69.0 2024