How to add a parameter to every URL Vanilla generates

JasonBarnabeJasonBarnabe Cynical Salamander ✭✭

I'm creating a plugin similar to Multilingual, but instead of having the locale chosen by user preference, I'm having it go only based on a request parameter. To make this work right, I need to add the locale parameter to every URL generated by Vanilla so that when users click around, they will always stick with the same locale.

What I've done is create a bootstrap.before.php file with:

<?php if (!defined('APPLICATION')) exit();

# Just like in functions.general.php, except add the locale
function Url($Path = '', $WithDomain = FALSE, $RemoveSyndication = FALSE) {
    $Result = Gdn::Request()->Url($Path, $WithDomain);
    $Result .= (strpos($Result, '?') ? '&' : '?') . 'locale=' . urlencode(GetValue('locale', $_GET, FALSE));
    return $Result;

This seems to add the parameter to most links, but many do not get the change, for example Categories, Recent Discussions, Activity, and the self-links to individual posts. Other links get mangled, like the jsConnect sign in URL, where the URL gets generated with two question marks.

I'm wondering if there's a more effective way to accomplish this. If not, would pull requests be accepted to make it so all URLs get passed through the Url function?


  • peregrineperegrine MVP
    edited October 2014

    if you don't get changes made with your pull requests....

    what about somehow adding it via jQuery.

          this.href = this.href + " add something that pulls a definition from somewhere";

    or something similar to

    or doing what you have and searching for locale and filling in if not present. and skipping when two question marks appear in string.

    I may not provide the completed solution you might desire, but I do try to provide honest suggestions to help you solve your issue.

  • JasonBarnabeJasonBarnabe Cynical Salamander ✭✭

    Adding it with JS is a good suggestion and may be part of the solution, but it would miss redirects, dynamically generated content, and of course wouldn't work with JS disabled.

    The problem with the jsConnect case is that it runs after my code, and its code is the one adding the second question mark.

  • BleistivtBleistivt MVP
    edited October 2014

    If you want to make some kind of preference persist, why not use


    You could still switch it with a url parameter by checking for that like so


    If you are also targeting unregistered users, I'd just set a cookie.

    My themes: pure | minusbaseline - My plugins: CSSedit | HTMLedit | InfiniteScroll | BirthdayModule | [all] - PM me about customizations

  • JasonBarnabeJasonBarnabe Cynical Salamander ✭✭

    That's what Multilingual does, and it's not what I want. I want URLs to be consistent in what language they load, as I think that's best practice and matches the site I'm integrating Vanilla into.

  • x00x00 MVP
    edited October 2014

    JasonBarnabe with respect this not smart. Not smart at all, and definitely not best best practice. You don't want that crap in links, it is not clean nor is it RESTful, it is not what GET parameters are for, persistent user setting across the site.

    Best practice would be either Session variable or submission/directory prefix, working with detection.

    The later you will handle with server rule, then you will detect it.

    Example or

    Where parameter are use at all, this would be an internal request such as for a legacy software.

    grep is your friend.

  • JasonBarnabeJasonBarnabe Cynical Salamander ✭✭

    I intend on making it a path prefix and have it rewritten through server rules, but I'm starting with a query parameter to keep things simple for testing. In either case, the problem remains the same - the Url function is not being called for all URLs, so I can't retain the path prefix/request parameter as the user navigates through the site.

  • hgtonighthgtonight ∞ · New Moderator

    This is an interesting issue.

    Why not do something like the API application and create controllers for all the supported languages. They all extend a base controller that will set redirect control to the "proper" controller. They also set the prefix to the real route via the URL function.

    You would end up with routes like en/discussions/unread and gb/discussions/unread that go to the same route discussions/unread.

    You still have the same issue as before with links that don't go through the Url function. Not necessarily better, but cleaner imo.

    Search first

    Check out the Documentation! We are always looking for new content and pull requests.

    Click on insightful, awesome, and funny reactions to thank community volunteers for their valuable posts.

  • If the locale identifier is in the path like @x00 suggested, it will appear in every url that vanilla creates

    RewriteRule  ^en/(.*)$  index.php\?p=$1&locale=en

    You might need to play around with C('Garden.WebRoot') or set it dynamically

    My themes: pure | minusbaseline - My plugins: CSSedit | HTMLedit | InfiniteScroll | BirthdayModule | [all] - PM me about customizations

  • JasonBarnabeJasonBarnabe Cynical Salamander ✭✭

    Thanks for the suggestion. I was thinking of using an Nginx rewrite rule to turn the path prefix into a query parameter by the time Vanilla got a hold of the URL, but I'll keep that in mind if that doesn't work out.

  • JasonBarnabeJasonBarnabe Cynical Salamander ✭✭

    (That was a reply to @hgtonight)

    @Bleistivt You think if I do the rewrite rule to turn path prefix into request parameter Vanilla will automatically generate the URL with path prefixes? I don't think it'll work like that, but I'll try a bit later and let you know.

  • @JasonBarnabe said:
    (That was a reply to hgtonight)

    Bleistivt You think if I do the rewrite rule to turn path prefix into request parameter Vanilla will automatically generate the URL with path prefixes? I don't think it'll work like that, but I'll try a bit later and let you know.

    It should work, but if not there are various environment variables you can set in nginx rule to force it to work, it is all depends how php is handled.

    grep is your friend.

  • JasonBarnabeJasonBarnabe Cynical Salamander ✭✭

    I have the nginx rewrite rule working, I can load my forum for example at /en/forum/ and my plugin can see locale=en as a parameter. This however has done nothing to the URLs generated - they still do not include the locale.

    I looked at Webroot as suggested by @Bleistivt‌. The only effect this seems to have is in combination with StripWebRoot, where the value set will be removed from the URL rather than added, which results in URLs like /discussion instead of /forum/discussion as it is now.

    I came across the reason why some URLs are not rewritten by my custom Url function - they get generated by a direct call to Gdn::Request()->Url. This seems limited to about 15 calls, so a pull request changing this wouldn't be too crazy. I'd like to know if this kind of change would be accepted before I pursue this avenue.

    I think I may have found a stupid hack to make it work, though. If I take the rewrite rule out and symlink /en/forum/ to /forum/, things seems to be working...

  • Like I said it can be done with server rules.


    fastcgi_param SCRIPT_FILENAME $document_root/en$fastcgi_script_name;

    inside the forum location block

    grep is your friend.

  • JasonBarnabeJasonBarnabe Cynical Salamander ✭✭
    edited October 2014

    No, that's the opposite of what I'm doing. That will make the URL /forum/ try to load the PHP scripts from /en/forum/. I want the URL /en/forum/ to load the PHP scripts from /forum/. The symlink method accomplishes this. Is there a way I can use a rule like that to strip the /en from $fastcgi_script_name?

  • x00x00 MVP
    edited October 2014

    Hold on the rule a bit more complex that that. let me get an example together.

    grep is your friend.

  • x00x00 MVP
    edited October 2014

    assume that forum is the location of you forum.

    create a file called alter_root.php in the forum root, add

    if(array_key_exists('locale', $_GET)){
        $_SERVER['PHP_SELF'] = '/'.$_GET['locale'].$_SERVER['PHP_SELF'];

    Add these location rules

    location ~ ^(/([a-z][a-z]))/forum {
        set $args $args&locale=$2;
        rewrite ^(/[a-z][a-z])/forum(/?(.*)) /forum/$2 last;
    location /forum {
        try_files $uri $uri/ @forum;
    location @forum {
        fastcgi_param PHP_VALUE "auto_prepend_file=$document_root/forum/alter_root.php";
        rewrite ^/forum/(.+)$ /forum/index.php?p=$1 last;

    grep is your friend.

  • JasonBarnabeJasonBarnabe Cynical Salamander ✭✭

    Thanks for that, at first glance it seems to be working. I'm going to test some more and will include it in the documentation when I release the addon.

Sign In or Register to comment.