LangRouter possibly interferes with multilingual setup


This is my second try of MODX (first one was some years ago and I’ve forgot everything related to it) so my troubles will probably seem childish… Sorry. Really want to keep this CMS my default one for making simple web sites.

I build 2-language website: default is Russian, and the second language is English. My web server is Nginx.
I Installed both Babel and LangRouter and followed their respective instructions which seem sane. Babel gives me the ability to interlink corresponging pages, and LangRouter the ability to hit a page based on the language of my choice. For LangRouter, I followed this manual: Usage - LangRouter . Friendly URLs are off now.
Now I have 2 Contexts, “web” being the default one (Russian) and “en” (English).

Seems like I have a problem with LangRouter which interfere with URIs from the client.

Home page / works well and I get it fine:


There, I have a link to some other page, say [[~2]], which ultimately gives me this URL:


And this URL gives me 404 generated by MODX (not Nginx). On the othe hand, this changed URL gives me what I request:


Note the absence of cultureKey in it.
I tried to debug it in the LangRouter code and it brought me some understanding of what it does under the hood, but still I can’t get it working. I think I miss something obvious here.
My understangind is this: LangRouter determines the desired language by parsing Request URI. If it finds cultureKey there, it uses it like that: if the current Context is of that same language it cuts cultureKey out of REQUEST_URI, otherwise it switches to that target Context and cuts cultureKey too. And if cultureKey isn’t found it determines it from Accept-Language client header and switches to that Context without tampering REQUEST_URI.
But despite cutting cultureKey from REQUEST_URI the final URI still contains it so it results in 404.

Can anyone suggest a solution or present their configs for a similar setup: MODX+Nginx+Multilingual?

I’m not sure that’s the problem, but have you tried it with friendly_urls set to Yes?

If I switch it on I get this link in html:
website/ru/about.html (page id=2 has “about” alias).
OK, and when I click it I get redirect loop as written by LangRouter into MODX error log. According to it, it first translates /ru/about.html into /ru/ and then iterates over it again and again.

I know it’s hard to grasp it all remotely so I will investigate further here but I appreciate any hints!

There is a debug setting in LangRouter that will log the results of langRouter in the MODX error log. Maybe that helps a bit.

If you get a redirect loop, you maybe are not allowed to see the target resource because it is unpublished and the error page too. It can also be caused by context access policies.

Thanks Thomas! I have LangRouter’s debug=on since its installation and always look at what it outputs into the log.
My redirect loop was possibly because I didn’t understand LangRouter’s logic and changed its code here and there to see if it make s any difference. Now I rolled back all my changes except additional debug lines.
I have all my Resources published and have no access policies explicitly defined and I double-checked that. Before multilingual, I had no problems requesting my pages.

After further plays with Nginx and MODX I’ve managed to make it work with FURLs=on but still unable to do the same with FURLs=off. Is LangRouter ever supposed to work without FURLs?
I decided to have 2 Nginx configs to have the one which works (FURLs=on) while playing with another for FURLs=off. The one that works now is built upon this example: Nginx Server Config - Using Friendly URLs | MODX Documentation .

root  C:/Web/sites/website/public/modx;
index index.php;

location / {
    if (!-e $request_filename) {
        rewrite ^/(.*)$ /index.php?q=$1 last;
# This location is specific for /{cultureKey}/index.php requests.
# Without it, you get Nginx's 404 on any link generated by, say, [[~2]].
location ~ /(ru|en)/index\.php$ {
    rewrite ^/(.*)$ /index.php?q=$1 last;
location ~ \.php$ {
    try_files $uri =404;
    fastcgi_split_path_info ^(.+\.php)(.*)$;
    fastcgi_index  index.php;
    include        fastcgi.conf;
    fastcgi_ignore_client_abort on;
    fastcgi_param  SERVER_NAME $http_host;

Now what’s not working. When you have FURLs=off all your links (in multilingual setup) are like these:


Resource 2 is available in the default “web” Context which is Russian.
Resource 12 is available in “en” Context which is English.
Both pages are published.

These links don’t work and respond with 404 (MODX responses, not Nginx’s thanks to that specific “location” in nginx.conf).
But if I remove cultureKey from them, then they work, at least those which are in the default “web” Context.

/index.php?id=12 (provided you have “en” Context as your default)

I now doubt it has something to do with LangRouter itself but it started afler implementing multilingual setup.
To summarize it: multilingual works with FURLs=on but does not with FURLs=off.

LangRouter’s log for /ru/index.php?id=2 is:

[2021-04-15 11:51:11] (ERROR in LangRouter @ C:\Web\sites\website\public\modx\core\components\langrouter\model\langrouter\langrouter.class.php : 158) Unhandled request:
REQUEST_URI:   /ru/index.php?id=2
QUERY_STRING:  q=ru/index.php&id=2
q:             ru/index.php
Context:       web
Site start:    1
[2021-04-15 11:51:11] (ERROR in LangRouter @ C:\Web\website\website\public\modx\core\components\langrouter\model\langrouter\langrouter.class.php : 175) contextmap: array(2) {
  string(3) "web"
  string(2) "en"
[2021-04-15 11:51:11] (ERROR in LangRouter @ C:\Web\sites\website\public\modx\core\components\langrouter\model\langrouter\langrouter.class.php : 158) Culture key found in URI:
REQUEST_URI:   /index.php?id=2
QUERY_STRING:  q=ru/index.php&id=2
q:             index.php
Context:       web
Site start:    1

Obviously, it successfully determine cultureKey from the request URI and cut it off but then MODX (?) cannot find the resource requested.

LangRouter is not built to work with FURLs off. Then a MODX generated resource url has no cultureKey prefix etc.

All context routing plugins I know work the same. First they detect the context on base of the http_host and the base_url. Then they remove the base_url and detect the resource by the remaining url path.

Now it explains everything. I think this should be reflected in the LangRouter documentation.
How do I mark this thread as solved?

Still, is it possible to manage non-FURL setup based on that same URL scheme?
/ru/index.php?id=2 (explicit cultureKey)
/index.php?id=2 (infer it from other sources)

Also, user language could be determined once and put into a cookie to be used by a routing plugin, so no cultureKey is ever required in URIs.

Thank you @jako and @halftrainedharry!

I don’t know much people, that use MODX with FURLs off and the url generation can’t be changed that easy in MODX.

The cookie solution would be a totally different approach.