We have a client who has had a brand new site developed by another agency, it is multilingual via some custom fields and some basic logic to display the relevant language via a simple language selector. No plugins involved.

The issue is that the client wants to ensure that their site has SEO that targets the various countries/languages they serve, but the current implementation is not SEO friendly whatsoever due to it being almost locale-adaptive (ie no specific URLs for each language).

Due to the costs, the client is not willing to rebuild the entire site with a plugin such as polylang/WPML etc, and we need to figure out a solution that can almost be dropped in and integrate with their existing fields/translations.

Our agency has proposed the following - we simply create a query variable "lang" that is then prepended to URLs if the selected language is not English, so if French is chosen, site.com/page becomes site.com/fr/page, site.com/blog-category/post becomes site.com/fr/blog-category/post etc. We can detect the query variable and serve the appropriate language.

I have got this partially working with some rewrite rules which I have little experience working with, where lang is a two character query variable:

// Register lang query var
function wf_register_query_vars( $vars ) {
    $vars[] = 'lang';
    return $vars;
add_filter( 'query_vars', 'wf_register_query_vars' );

// Add rewrite rules
function wf_rewrite_tag_rule() {
    add_rewrite_tag( '%lang%', '([a-z]{2})' );
    add_rewrite_rule( '^([a-z]{2})/?$', 'index.php?lang=$matches[1]', 'top' );
    add_rewrite_rule( '^([a-z]{2})/?$', 'index.php?lang=$matches[1]&page_id=12', 'top' ); // Home page
    add_rewrite_rule( '^([a-z]{2})/example/?$', 'index.php?lang=$matches[1]&post_type=example', 'top' ); // Example CPT Archive
    add_rewrite_rule( '^([a-z]{2})/example/(.?.+?)(/[0-9]+)?/?$', 'index.php?lang=$matches[1]&example=$matches[2]', 'top' ); // Example CPT
    add_rewrite_rule( '^([a-z]{2})/(.?.+?)(/[0-9]+)?/?$', 'index.php?lang=$matches[1]&name=$matches[2]&page=$matches[3]', 'bottom' ); // Posts
    add_rewrite_rule( '^([a-z]{2})/(.?.+?)(/[0-9]+)?/?$', 'index.php?lang=$matches[1]&pagename=$matches[2]&page=$matches[3]', 'top' ); // Pages
add_action('init', 'wf_rewrite_tag_rule', 10, 0);

With the above, the home page works (it's a static page set as the home page), as do single "post" post-types, and single "example" post types. Pages do not work however (404), and have not yet begun looking into archives, categories, tags, taxonomies etc.

If I swap the order of the bottom two (posts and pages), posts will stop working (404), but pages will start working.

So the question is, is there a way to add a rewrite rule that just covers everything - pages, posts, archives, custom post types, custom taxonomies, pagination etc?

I can handle all other logic (serving 404's for non-existent translations, generating hreflangs, changing WP's locale, html lang attribute etc), it is just the rewrite rules that have left me absolutely stumped!

1 Answer 1


I don't know if this can be done at once --using maybe an htaccess rule--.

But, I think rebuilding the existing rewrite rules instead of adding new ones can do the trick.

There are filters available for each group of rewrite rules:


By using 'post_rewrite_rules' filter we can rebuild the post rewrite rules using a code like this:

add_filter('post_rewrite_rules', function($rules){
    $newRules = [];
     * Loop through the current set of rules and:
     * 1- Prepend the language matching regex
     * 2- Increment the current matched groups by one each
     * 3- Append the matched language to the query as the matched group 1
     * 4- Add the modified rule to the $newRules array
    foreach ($rules as $regex => $query) {
        $newRules['^([a-z]{2})/' . $regex]
            = str_replace(
                ['[2]', '[1]'],
                ['[3]', '[2]'], $query) . '&lang=$matches[1]';
    return $newRules;

For the changes to be applied; rewrite rules should be flushed. Also the post permalink should be modified by adding the current language to it, may be using a code like this:

add_filter('post_link', function ($link){
    $lang = get_query_var('lang', 'en');
            home_url('/' . $lang . '/'),

Your Answer

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

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