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?

Comments

s-ude created an issue. See original summary.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.1.10 (June 4, 2021) and Drupal 9.2.10 (November 24, 2021) were the last bugfix releases of those minor version series. Drupal 9 bug reports should be targeted for the 9.3.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

jhuhta’s picture

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.

dspachos’s picture

Perhaps related: #3314941

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.