2

basically what i have in my .htaccess is couple of lines to detected the selected fake*sub-folder language requested in the URI and redirect it automatic to ?lang=(selected language)

RewriteEngine on
RewriteBase /bb/

RewriteCond ^(fr|en|ar)/?(.*)?$ !-d
RewriteRule ^en/?(.*)?$ ./$1?lang=English [L,QSA]
RewriteRule ^fr/?(.*)?$ ./$1?lang=French  [L,QSA]
RewriteRule ^ar/?(.*)?$ ./$1?lang=Arabic  [L,QSA]

so any URL look like this one for example : localhost/bb/fr/

will header to localhost/bb/index.php?lang=French in background but keeping the requested fake*sub-folder in the same time exist in the physical url

and it works perfect but there is still something necessary to do which is :

if no language is selected and the user is on the url : localhost/bb/ or localhost/bb/index.php it should redirect him by default to English (localhost/bb/en) since he didn't select any

what i tried is :

RewriteCond !^(fr|en|ar)/?(.*)?$ !-d
RewriteRule !^(fr|en|ar)/?(.*)?$ ./en/$1 [L,R]

but that causes a weird infinite loop that look like : localhost/bb/en/en/en/en/en/en/en/en/en/en/en/en/?lang=English?lang=English?lang=English?lang=English?lang=English?lang=English

same result if i do :

RewriteRule ^(?!(?:en|ar|fr)/)(.*)$ en/$1 [R=301,L]

and if i use something like :

RewriteCond %{REQUEST_URI} !^/((en|ar|fr)/.*)$
RewriteRule .* ./en/%1 [L,R=301]

which is suggested by other Q-answers in the community here

it fire an Error like : localhost redirected you too many times.

Any Suggestions or solutions with Examples will be appreciated a lot .

2
  • 1
    If a user was to request /bb/fr/foo, what is the expected result? Or is "foo" always expected to be a valid file?
    – MrWhite
    Mar 5, 2020 at 17:20
  • anything after /fr/ will be a valid request or a valid file, like /bb/fr/?article=123 or bb/fr/contact.php Mar 5, 2020 at 21:55

1 Answer 1

1

From your directives, I assume your .htaccess file is located inside the /bb subdirectory.

RewriteCond ^(fr|en|ar)/?(.*)?$ !-d
RewriteRule ^en/?(.*)?$ ./$1?lang=English [L,QSA]
RewriteRule ^fr/?(.*)?$ ./$1?lang=French  [L,QSA]
RewriteRule ^ar/?(.*)?$ ./$1?lang=Arabic  [L,QSA]

Whilst these directives might be "working perfectly", they are not "correct".

The RewriteCond directive is always successful, since ^(fr|en|ar)/?(.*)?$ (which is a literal string) will never map to a directory. You probably meant to use %{REQUEST_FILENAME} here. However, since you state that the sub-folder language is "fake", the request could never map to a real directory anyway (providing it matched the RewriteRule pattern) so this could probably just be removed. Also, the RewriteCond directive only applies to the first RewriteRule that follows, so the 2nd and 3rd rules are working unconditionally anyway.

Also, the substitution string should not be prefixed with ./ (to signify the current directory) - it still works because the OS is able to resolve this, but otherwise it is just "bloat". eg. This would otherwise become /bb/./foo?lang=English, which is the same as /bb/foo?lang=English.

With the sub-regex (.*)? - the trailing ? is not required since (.*) is optional by definition (0 or more instances of .).

if no language is selected and the user is on the url : localhost/bb/ or localhost/bb/index.php it should redirect...

Your attempts seem to focus on testing that no language is in the URL, but if the user is on /bb/ or /bb/index.php then no language is present anyway - so just check for either of these two URLs? (Or have I missed something? Maybe if a URL of the form /bb/foo/bar is requested - but you don't appear to have mentioned that?)

Try the following instead:

RewriteEngine on
RewriteBase /bb/

# Default to English (ie. /bb/en)
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(index\.php)?$ en/ [R,L]

# Route language URLs
RewriteRule ^en/?(.*) $1?lang=English [L,QSA]
RewriteRule ^fr/?(.*) $1?lang=French  [L,QSA]
RewriteRule ^ar/?(.*) $1?lang=Arabic  [L,QSA]

The condition on the first rule is to ensure we don't get a redirect loop (after rewriting the URL later).

UPDATE:
...by changing the code a bit RewriteRule !^(fr|en|ar)/?(.*)$ en/$1 [R,L] to avoid the loop...

Unless you still have the condition (RewriteCond directive) that I included above, that rule by itself looks like it would result in a redirect loop if requesting a URL of the form /bb/en/, since the later rewrite rewrites the URL to /bb/?lang=English which would get caught by the earlier redirect (when the rewrite engine starts over)? Rinse and repeat... (?)

However, if this directive is working OK for you then it can be simplified...

  • Negated (ie. ! prefix) patterns cannot actually capture anything (by definition, they are non-matching), so the trailing capturing sub-pattern (.*) is not capturing anything.
  • Likewise, the $1 backreference in the substitution string is always empty.

So, this could be rewritten like this:

RewriteRule !^(fr|en|ar)/? en/ [R,L]

anything after /fr/ will be a valid request or a valid file, like /bb/fr/?article=123 or bb/fr/contact.php

You could default the language if requesting /bb/<something> eg. /bb/contact.php redirect to /bb/en/contact.php.

For example:

# Default to English (ie. /bb/en/<anything>)
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} ^/bb/(.*)
RewriteRule !^(en|fr|ar)/? en/%1 [R,L]

The %1 backreference (as opposed to $1) is a backreference to the last matched CondPattern, ie. the part of the URL after /bb/ (if anything).

EDIT: I've added the filesystem conditions to exclude requests for actual directories and files.

if i request the link without the last slash like this: http://localhost/bb/en it show me the page with broken images and css links

That actually answers a query I had... I wasn't sure whether the trailing slash on the language code was required or not (since it was optional in your RewriteRule pattern) - so I had omitted it on the first redirect initially (I've since included the trailing slash after the "update").

The broken images and CSS links are most probably the result of using relative URL paths in your client-side HTML. You should use root-relative (or absolute) URLs when URL-rewriting to avoid issues like this. See my answer to the following Webmasters question for more detail on this:

The trailing slash shouldn't really be optional (it essentially creates two URLs that serve the same content). Which one do you link to - which is canonical? I assume a trailing slash is canonical... you could then enforce a trailing slash in .htaccess, before the exitsing directives. For example:

# Force trailing slash on language code
RewriteRule ^(en|fr|ar)$ $1/ [R=301,L]

In Summary

Applying the recent points...

RewriteEngine on
RewriteBase /bb/

# Force trailing slash on language code
RewriteRule ^(en|fr|ar)$ $1/ [R=301,L]

# Default to English (ie. /bb/en/<anything>)
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} ^/bb/(.*)
RewriteRule !^(en|fr|ar)/? en/%1 [R,L]

# Route language URLs
RewriteRule ^en/(.*) $1?lang=English [QSA,L]
RewriteRule ^fr/(.*) $1?lang=French  [QSA,L]
RewriteRule ^ar/(.*) $1?lang=Arabic  [QSA,L]
6
  • Awesome man your Explanation and the code example you provided was very helpful, i managed to do it by changing the code a bit RewriteRule !^(fr|en|ar)/?(.*)$ en/$1 [R,L] to avoid the loop, so now whatever i call http://localhost/bb/ or http://localhost/bb/index.php the link jump automatic to http://localhost/bb/en/ but there was another problem show up after i did that , if i request the link without the last slash like this: http://localhost/bb/en it show me the page with broken images and css links, example ! what could be the reason for this ? Mar 5, 2020 at 20:05
  • offf its actually now redirecting even actual sub folders like admin/ to bb/en :( Mar 6, 2020 at 1:19
  • 1
    I've updated my answer to address the issues you've raised. Including your last comment about redirecting actual subfolders (that's a result of redirecting anything that is not "fr", "en" or "ar") - you can add some filesystem conditions to resolve that.
    – MrWhite
    Mar 6, 2020 at 1:38
  • the English wasn't forced as default language when applying the lines` %{REQUEST_FILENAME} ...` , but after i removed the line RewriteCond %{REQUEST_FILENAME} !-f now everything working perfect , i appreciate spending a long time to write that answer and to explain all of this not only to benefit me but also to benefit other people, that's a teacher mind, thank you so much you are literally genius , i appreciate this Mar 6, 2020 at 9:36
  • If you were having to remove the RewriteCond %{REQUEST_FILENAME} !-f condition then that would seem to suggest that you are requesting files that exist outside the virtual language folder but still need to be redirected to the virtual language folder (ie. /en/)? However, looking back at this, it doesn't look like it will redirect (ie. default the language of) the bare directory ie. localhost/bb/? (Since that is a directory and the condition fails.) If that is an issue then it may be easier to create a separate rule for that scenario?
    – MrWhite
    Mar 12, 2020 at 23:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.