Mod_rewrite replace underscores with dashes (or hyphens) in Opencart

I'm transferring an e-commerce site to a new shopping basket. For some reason the old site used dashes for categories but underscores for products.

So the full URL of a product looks like this:

http://www.example.com/Engineering-Common-Bricks/65mm_Class_B__Solid_Engineering_Brick__Price_Each

The new basket uses dashes for everything so I need to rewrite incoming URLs such as the above to the following:

http://www.example.com/Engineering-Common-Bricks/65mm-Class-B--Solid-Engineering-Brick--Price-Each

I know there is a lot of material on rewriting underscores to dashes but none of them seem to be working for me.

I am using Opencart which comes with an existing set of rewrite rules so perhaps these are interfering with the new rules I'm trying to add.

The existing .htaccess is as follows:

Options +FollowSymlinks

# Prevent Directoy listing 
Options -Indexes

# Prevent Direct Access to files
<FilesMatch "\.(tpl|ini|log)">
 Order deny,allow
 Deny from all
</FilesMatch>

# SEO URL Settings
RewriteEngine On
# If your opencart installation does not run on the main web folder make sure you folder it does run in ie. / becomes /shop/ 

RewriteBase /
RewriteRule ^sitemap.xml$ index.php?route=feed/google_sitemap [L]
RewriteRule ^googlebase.xml$ index.php?route=feed/google_base [L]
RewriteRule ^download/(.*) /index.php?route=error/not_found [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !.*\.(ico|gif|jpg|jpeg|png|js|css)
RewriteRule ^([^?]*) index.php?_route_=$1 [L,QSA]

Any help or direction would be gratefully received.


Updating the .htaccess as per Esa's answer to the following:

Options +FollowSymlinks
Options -Indexes

<FilesMatch "\.(tpl|ini|log)">
 Order deny,allow
 Deny from all
</FilesMatch>

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^_]*)_([^_]*_.*) $1-$2 [N]
RewriteRule ^([^_]*)_([^_]*)$ /$1-$2 [L,R=301]

RewriteBase /
RewriteRule ^sitemap.xml$ index.php?route=feed/google_sitemap [L]
RewriteRule ^googlebase.xml$ index.php?route=feed/google_base [L]
RewriteRule ^download/(.*) /index.php?route=error/not_found [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !.*\.(ico|gif|jpg|jpeg|png|js|css)
RewriteRule ^([^?]*) index.php?_route_=$1 [L,QSA]

Works fine for:

http://example.com/this_is_a_category

But causes Apache to crash on:

http://example.com/this_is_a_category/this_is_a_product

Solution 1:

Since RewriteRules are for regular expression matches instead of replaces, the first line in

RewriteRule ^([^_]*)_([^_]*_.*) $1-$2 [N]
RewriteRule ^([^_]*)_([^_]*)$ /$1-$2 [L,R=301]

is not repeated. Therefore you will need one line for every amount of underscores, e.g.

RewriteRule ^(.*)_(.*)_(.*)_(.*)_(.*)_(.*)_(.*)$ /$1-$2-$3-$4-$5-$6-$7 [R=301,L]
RewriteRule ^(.*)_(.*)_(.*)_(.*)_(.*)_(.*)$ /$1-$2-$3-$4-$5-$6 [R=301,L]
RewriteRule ^(.*)_(.*)_(.*)_(.*)_(.*)$ /$1-$2-$3-$4-$5 [R=301,L]
RewriteRule ^(.*)_(.*)_(.*)_(.*)$ /$1-$2-$3-$4 [R=301,L]
RewriteRule ^(.*)_(.*)_(.*)$ /$1-$2-$3 [R=301,L]
RewriteRule ^(.*)_(.*)$ /$1-$2 [R=301,L]

replaces 1-5 underscores, resulting in

/Engineering_Common_Bricks/65mm_Class_B-Solid-Engineering-Brick-Price-Each

so for 9 replaces you'll need a few more lines to the top. The pattern will be same, but the longer expressions needs to become first.

However, this replaces all underscores with dashes (and redirects after the last one), so you must not have any file names containing underscores. You could also prevent that happening by adding a RewriteCond directive BEFORE these rules:

RewriteCond %{REQUEST_FILENAME} !-f