htaccess url 重写虚拟语言文件夹将页面名称加倍为 URL 中的文件夹

问题描述 投票:0回答:2

我有一个只有 3 个页面的小型 PHP 网站。页面内容动态翻译成荷兰语或英语(我从 URL 中获取语言)

\index.php
\page-one.php
\page-two.php

我要实现以下网址的

https://www.example.com/ => https://www.example.com/en/ or nl/ depending browser language
https://www.example.com/en/ => \index.php
https://www.example.com/en/page-one/ => \page-one.php
https://www.example.com/en/page-two/ => \page-two.php
https://www.example.com/nl/ => \index.php
https://www.example.com/nl/page-one/ => \page-one.php
https://www.example.com/nl/page-two/ => \page-two.php

它在我的 PC 上本地工作,使用 WAMP 和以下 htaccess

RewriteEngine On
RewriteBase /

RewriteCond %{REQUEST_URI} !(/$|\.) 
RewriteRule (.*) %{REQUEST_URI}/ [R=301,L] 

RewriteCond %{HTTP:Accept-Language} ^nl
RewriteCond %{THE_REQUEST} \ /+(?!(en|nl)/).*
RewriteRule ^(.*)$ /nl/$1 [L,R]
RewriteRule ^nl/(.*)$ /$1 [L]

RewriteCond %{THE_REQUEST} \ /+(?!(en|nl)/).*
RewriteRule ^(.*)$ /en/$1 [L,R]
RewriteRule ^en/(.*)$ /$1 [L]

但是,当我将其发布在共享虚拟主机(在 OVH)上时,页面名称的子文件夹指向索引文件

OK https://www.example.com/ => https://www.example.com/en/ or nl/
OK https://www.example.com/en/ => \index.php
NOK https://www.example.com/en/page-one/ => \index.php
NOK https://www.example.com/en/page-two/ => \index.php
same for the /nl/

页面仅显示如下

https://www.example.com/en/page-one/page-one/ => \page-one.php
https://www.example.com/en/page-two/page-two/ => \page-two.php

还有这些网址的作品,不应该这样

https://www.example.com/en/page-one/page-two/ => \page-two.php
https://www.example.com/en/page-two/page-one/ => \page-one.php

好像跑了两次htaccess的第10行和第14行

我该如何解决这个问题?

regex apache .htaccess mod-rewrite
2个回答
1
投票

这看起来像是与

MultiViews
(mod_negotiation 的一部分)的冲突。这将解释这是如何在本地工作的,以及实时服务器上的行为差异(我怀疑未启用 MultiViews)。 (虽然它似乎没有解释
/en/page-one/page-one/
如何在实时服务器上看似“工作”?但是,这将在启用多视图的情况下在本地工作。这同样适用于
/en/page-one/page-two/
- 更多内容见下文。)

在您的 mod_rewrite 指令中,您在任何时候都没有附加

.php
扩展名,因此它们本身不可能工作(除非您请求
page-one.php
- 使用
.php
扩展名 - 直接)。所以,看起来你依赖于 MultiViews(它有效地附加了文件扩展名)。

还有这些网址的作品,不应该这样

https://www.example.com/en/page-one/page-two/ => \page-two.php
https://www.example.com/en/page-two/page-one/ => \page-one.php

MultiViews 允许这样的东西“工作”。虽然我希望这是相反的方式。 IE。

/en/page-one/page-two/
会服务
/page-one.php
,而不是你建议的
page-two.php

这里发生的是您的 mod_rewrite 规则在内部将对

/en/page-one/page-two/
的请求重写为
/page-one/page-two/
。然后 MultiViews 发起一个内部子请求到
/page-one.php/page-two/
/page-two/
只是 PATH-INFO)并且
/page-one.php
被提供。


您需要确保禁用了 MultiViews。然后在适当的地方手动附加

.php
扩展名。但是,您没有说明您是如何管理静态资产(JS、CSS、图像等)的?这些是否应该受到相同的重定向/重写?这些语言也是特定的吗?

我会假设你直接链接到静态资产,所以这些不应该受到 URL 重写。

请尝试以下操作:

# Ensure that MultiViews is disabled
Options -MultiViews

DirectoryIndex index.php

RewriteEngine On

# Abort early if request has already been rewritten
RewriteCond %{ENV:REDIRECT_STATUS} .
RewriteRule ^ - [L]

# Abort early if request maps to a file OR directory (except root)
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule . - [L]

# Append trailing slash to non-assets
RewriteCond %{REQUEST_URI} !(/$|\.) 
RewriteRule . %{REQUEST_URI}/ [R=301,L]

# Prefix request with language code if omitted (302 - temporary)
# (Defaults to "en" if not "nl" or omitted)
RewriteCond %{HTTP:Accept-Language}@en (?:^|@)(nl|en)
RewriteRule !^(en|nl)/ /%1%{REQUEST_URI} [R=302,L]

# Rewrite to remove language prefix and append ".php" extension if file exists
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^(?:en|nl)/([^/]+)/$ $1.php [L]

# Otherwise, rewrite to remove the language prefix (handles the DirectoryIndex)
RewriteRule ^(?:en|nl)/(.*) $1 [L]

不需要原始规则块中的

RewriteBase
指令(这里也不需要)。

无需检查

THE_REQUEST
,因为我们在请求已经被内部重写时提前中止(通过检查
REDIRECT_STATUS
环境变量 - 在初始请求中为空,并在第一次成功重写后将其设置为 HTTP 状态).

如果在 Apache 2.4 上,那么您可以在最后两条规则(均重写)上使用

END
标志而不是
L
并删除第一个检查
REDIRECT_STATUS
env var 的“提前中止”规则。
END
标志停止所有处理,因此不会发生“循环”。

进一步改进...如果直接请求

index.php
page-one.php
page-two.php
(即任何具有
.php
扩展名的内容),请考虑重定向请求。


0
投票

基于@MrWhite 他的回应,以下 .htaccess 做了预期的事情

Options -MultiViews

DirectoryIndex index.php

RewriteEngine On

# Append trailing slash to non-assets
RewriteCond %{REQUEST_URI} !(/$|\.)
RewriteRule . %{REQUEST_URI}/ [R=301,L]

RewriteCond %{HTTP:Accept-Language} ^nl
RewriteCond %{THE_REQUEST} \ /+(?!(en|nl)/).*
RewriteRule ^(.*)$ /nl/$1 [L,R=302]

RewriteCond %{THE_REQUEST} \ /+(?!(en|nl)/).*
RewriteRule ^(.*)$ /en/$1 [L,R=302]

# Abort early if request has already been rewritten OR maps to a file OR directory
RewriteCond %{ENV:REDIRECT_STATUS} . [OR]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

# Rewrite to remove language prefix and append ".php" extension if file exists
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^(?:en|nl)/([^/]+)/$ $1.php [L]

# Otherwise, rewrite to remove the language prefix (handles the DirectoryIndex)
RewriteRule ^(?:en|nl)/(.*) $1 [L]
© www.soinside.com 2019 - 2024. All rights reserved.