Ask your WordPress questions! Pay money and get answers fast! Comodo Trusted Site Seal
Official PayPal Seal

WordPress: Custom rewrite rules WordPress

  • SOLVED

Hi,

I'm trying to create custom rewrite rules for some Custom Post Types that I'm using on a site. The site is multilingual so each CPT require a language setting in order to be displayed correctly when browsing the site (I chose not to create two individual posts in order to save time for the client when publishing - the posts have lots of options).

The goal is to make the following rewrites:

<em>fastigheter-spanien/X -> property/X&lang=sv
fastigheter-usa/X -> property/X&lang=sv
properties-spain/X -> property/X&lang=en
properties-usa/X -> property/X&lang=en</em>

Where X is the slug for the CPT. Basically I just want to add a language setting and hide the ugly "property" in the url, either by modifying .htaccess or adding rewrite rules in functions.php. I've tried both on my own but just can't manage to solve it.

Thanks in advance.

WordPress version 3.0

Answers (4)

2010-07-31

Mike Schinkel answers:

@Staffan: You can use <em>mod_rewrite/.htaccess</em> if you want but I prefer to keep things within WordPress if possible. I put the full code you would drop into your theme's <em>functions.php</em> file here:

[[LINK href="http://gist.github.com/502421"]]http://gist.github.com/502421[[/LINK]]

To explain, first step is to use an "<em>init</em>" hook is to run the following five lines of code. The 2nd and 3rd lines of code add "lang" both as a query var and a rewrite tag so that WordPress is aware of it and can process it. Then next two lines tell WordPress about your two new URL formats (this code assumes your "<em>property</em>" post type has been set and has a query_var of "<em>property</em>":


global $wp,$wp_rewrite;
$wp->add_query_var('lang');
$wp_rewrite->add_rewrite_tag('%lang%','([^/]+)','lang=');
$wp_rewrite->add_permastruct('fastigheter', 'fastigheter-%lang%/%property%');
$wp_rewrite->add_permastruct('properties', 'properties-%lang%/%property%');


After this you'll need to run the following line of code, but you need only do it once otherwise it will make your pages load slow:

$wp_rewrite->flush_rules(false);


An alternate to running <em>flush_rules()</em> then commenting it out would be to just update your permalinks once after adding the six lines above here:

[[LINK href="http://example.com/wp-admin/options-permalink.php"]]http://example.com/wp-admin/options-permalink.php[[/LINK]]

(replace <em>example.com</em> with your domain)

Next you need to snag the value of "<em>lang</em>" which will come in as "<em>spanien</em>", "<em>spain</em>" or "<em>usa</em>" and convert it to "<em>sv</em>", "<em>sv</em>" or "<em>en</em>", respectively using a "<em>request</em>" hook:

add_filter('request', 'my_request');
function my_request($request) {
if (isset($request['lang']))
switch ($request['lang']) {
case 'spanien':
case 'spain':
$request['lang'] = 'sv';
break;
case 'usa':
default:
$request['lang'] = 'en';
}
return $request;
}


Finally I added the function <em>get_property_lang()</em> to make it easy to grab the value of "<em>lang</em>" in your theme or plugin. Since this "<em>lang</em>" query var is not actually a real query var from the perspective of the web server you won't see it in <em>$_GET['lang']</em> but instead in <em>$wp_query->query_vars['lang']</em>. (I could have stuffed in into <em>$_GET</em> but that's not a great idea as some plugins may expect it to be what came in the URL from the browser. Besides, the better WordPress devs use "<em>query_vars</em>" anyway):

function get_property_lang() {
global $wp_query;
return (isset($wp_query->query_vars['lang']) ? $wp_query->query_vars['lang'] : '');
}


Hope this helps, let me know.

P.S. <strong>VERY IMPORTANT</strong>: The first time try this it won't work because the rewrite rules won't be active yet. If it doesn't appear to work be sure the rules are flushed by saving your permalinks or running the <em>flush_rules()</em> again.


Staffan Estberg comments:

Wow, thank you for that detailed answer! I'm beginning to feel embarrassed for tossing out a lousy $10 for all the help I've received.

Something's fishy because it's not working. The way I interpret your code it has to. I don't know if it takes time for the rewrite rules to become active but I've tried updating the permalinks several times as well as running the flush included in the functions. Still get to "property/x" when trying to use one of the language urls.


Mike Schinkel comments:

Are you saying you the code I provided does a redirect back to the <em>/property/%property%</em> url, or that it doesn't work at all.

Have you made sure to clear out all the code the other people provided; it might cause conflicts.


Staffan Estberg comments:

Well I'm not sure whether it's working (and doing a redirect back, which would be weird) or not. I only use your code at the moment, and I've double checked in the .htaccess where I made some changes earlier.


Staffan Estberg comments:

Note that I'm using WPML, if this could affect rewrites in any way.


Mike Schinkel comments:

I have no idea if WPML affects it. Can you disable to set what it does?


Mike Schinkel comments:

Also, are you on Windows? Mac? Other?


Mike Schinkel comments:

I just installed WPML and added Spanish, and it seems to work.


Staffan Estberg comments:

Mac. Using Firefox and Chrome. Could check with Safari but it shouldn't be a browser issue.


Mike Schinkel comments:

Where did you put my code? In <em>functions.php</em>? Can you post your file to a [[LINK href="http://gist.github.com/"]]gist[[/LINK]] so I can see it?


Mike Schinkel comments:

@Steffan: On Mac there is unfortunately no good free HTTP sniffer, but you can get a 14 day trial of [[LINK href="http://www.tuffcode.com/"]]HTTPScoop[[/LINK]]. It will let you see what is going on at the HTTP level (on Windows there is the excellent and free [[LINK href="http://www.fiddler2.com/fiddler2/"]]Fiddler[[/LINK]].)


Staffan Estberg comments:

http://gist.github.com/502599

I created my theme based on Twentyten and have not removed any of the functions from that theme. But I've looked through that code and there are no rewrites or any other possible conflicts that I can see.


Mike Schinkel comments:

Awesome, I'm testing on TwentyTen. I took your functions.php verbatim and used it instead of mine and everything still works. Let's walk through it. I created a "<em>property</em>" called "<em>Test</em>" and I can load it with any of these URLs (replace "<em>example.com</em>" with your domain):

http://example.com/fastigheter-spanien/test/
http://example.com/fastigheter-usa/test/
http://example.com/properties-spain/test/
http://example.com/properties-usa/test/
http://example.com/property/test/


I've created a theme file "<em>single-property.php</em>" which is just a copy of "<em>single.php</em>" and I've added the following like just below the "<em>&lt;div id="content" role="main"&gt;</em>":

<font size="7">[<?php echo get_property_lang(); ?>]</font>


When I use one of the first four URLs is displays them as I expected.

BTW, you don't need to duplicate the call to "<em>register_post_type('property',...)</em>" that I added so that I could test it since you already have it.

So, when you try those first 4 URLs do they redirect to the 5th or give you page not found?

What other plugins are you using?


Staffan Estberg comments:

Thanks for looking into that. I tried your test urls and I end up with a 404, which sounds better to me then an "ignored" redirect.

As for other plug-ins, the only one besides WPML is Simple Fields, a little admin manager for adding custom fields to custom post types.


Mike Schinkel comments:

So I added Simple Fields and it is still working perfectly for me.

Is your site online where I can see it not working? (maybe I can discover something from seeing it.)


Staffan Estberg comments:

Check your messages, I sent you the URL there.


Mike Schinkel comments:

Let's try a different approach. Try this functions.php instead:

[[LINK href="http://gist.github.com/502677"]]http://gist.github.com/502677[[/LINK]]

Note the code below includes killing the "<em>my_request</em>" function I previously provided you as well as it's <em>add_filter()</em> call and adding this new hook:

add_action('icl_set_current_language', 'my_icl_set_current_language');
function my_icl_set_current_language($lang) {
$selector = preg_replace('#^(.+)-(spanien|spain|usa)/(.*)$#','$2',$_SERVER['REQUEST_URI']);
switch ($selector) {
case 'spanien':
case 'spain':
$lang = 'sv';
break;
case 'usa':
$lang = 'en';
break;
}
return $lang;
}


Let me know if is works.


Mike Schinkel comments:

Ugh! This is looking like it is a problem with WPML. At first glance at least, they've made some code decisions that are not so good. They have a hook called '<em>icl_set_current_language</em>' which looks like what you need to call but you can only call it from a plugin because it is itself called from the '<em>plugins_loaded</em>' hook. Seesh!

Let's go offline to discuss since you've already sent me a direct message about it.

2010-07-31

Deepak Thomas answers:

Try adding this to the top of your .htaccess

<IfModule mod_rewrite.c>
RewriteRule ^fastigheter-spanien/([^\&]+) property/$1&lang=sv [NC,L]
RewriteRule ^fastigheter-usa/([^\&]+) property/$1&lang=sv [NC,L]
RewriteRule ^properties-spain/([^\&]+) property/$1&lang=en [NC,L]
RewriteRule ^properties-usa/([^\&]+) property/$1&lang=en [NC,L]
</IfModule>


Staffan Estberg comments:

You mean before the # BEGIN WordPress? That didn't work. I also tried including the rules within WordPress' IfModule but it didn't help.


Deepak Thomas comments:

I'd edited the code after posting it. Did you try the one that's presently visible?

RewriteRule ^fastigheter-spanien/([^\&]+) property/$1&lang=sv [NC,L]
RewriteRule ^fastigheter-usa/([^\&]+) property/$1&lang=sv [NC,L]
RewriteRule ^properties-spain/([^\&]+) property/$1&lang=en [NC,L]
RewriteRule ^properties-usa/([^\&]+) property/$1&lang=en [NC,L]


Staffan Estberg comments:

Yes, but where exactly should I put it?

2010-07-31

Rashad Aliyev answers:

For advanced ReWrite rules you can use this plugin.

http://wordpress.org/extend/plugins/wp-htaccess-control/screenshots/


Staffan Estberg comments:

I'd rather not use a plug-in for such a small change. But thanks for the tip.

2010-07-31

Naif Amoodi answers:

You don't need to edit the .htaccess file for this. You can do it using the API provided by WordPress. The following should basically do what you are after. Add to following to your functions.php file and make appropriate modifications because I have only used the details that were provided:


add_action('init', 'your_init');
add_filter('rewrite_rules_array', 'your_rewrite_rules_array');
add_filter('query_vars', 'your_query_vars');

function your_init() {
global $wp_rewrite;

$wp_rewrite->flush_rules();
}
function your_rewrite_rules_array($rewrite_rules) {
global $wp_rewrite;

$custom['(fastigheter-spanien)/(.+)/?$'] = 'index.php?property=$matches[2]&language=sv';
$custom['(fastigheter-usa)/(.+)/?$'] = 'index.php?property=$matches[2]&language=sv';
$custom['(properties-spain)/(.+)/?$'] = 'index.php?property=$matches[2]&language=en';
$custom['(properties-usa)/(.+)/?$'] = 'index.php?property=$matches[2]&language=en';

return $custom + $rewrite_rules;
}
function your_query_vars($query) {
array_push($query, 'language');

return $query;
}


After making the changes, make sure you visit the permalinks section in the WordPress administration panel so that the rewrite flush can take effect. And once you are able to get things to work, comment out the line add_action('init', 'your_init'); because you don't want your rewrite rules getting flushed every time the site is accessed.


Staffan Estberg comments:

Nice! I think the actual rewrite is working but it shows the "property" url. I'd like to keep the "properties-spain" (or one of the others) url.


Naif Amoodi comments:

It probably isn't working then. If you try accessing the URL, /fastigheter-spanien/somename/, WP shouldn't perform a redirect. The URL should stay intact. Try doing the following one at a time. And once you perform every step, make sure to access the permalinks page from the admin panel to make sure WP registers the rewrite changes.

1. Try replacing array_push($query, 'language'); with array_push($query, 'property', 'language');
2. If that doesn't help, try removing references to /?

Remember not to comment out the line I asked you to until what you are trying to do works. Everytime you make a change, that line needs to be called and you need to access the permalinks section from the administration panel.


Staffan Estberg comments:

Sorry but it still doesn't work. This is driving me nuts.


Naif Amoodi comments:

Lets try removing canonical urls. Add the following to your functions.php file:


if(!empty(get_query_var('language'))) remove_action('template_redirect', 'redirect_canonical');


I believe that should do the trick. The conditional statement there checks if it's our 'custom url'. And no need of using array_push($query, 'property', 'language'); if array_push($query, 'language'); works