Rails link to current page and passing parameters to it

Link to current page with different locales

Tested on Rails 4

Hello all. After some time of research I decide to write my own solution for this.

link_to 'English', url_for( :locale => 'en' )
link_to 'Deutch', url_for( :locale => 'de' ) 

This works perfect, but it allows XSS Vulnerability just passing parameters in your URL like below:

http://localhost:3000/en/about?host=www.fishingsiteorbadurl.com/%23&port=80

Or worst case:

http://localhost:3000/en/about?host=%D0%BE%D1%87%D0%B5%D0%BD%D1%8C%D0%BF%D0%BB%D0%BE%D1%85%D0%BE%D0%B9%D1%81%D0%B0%D0%B9%D1%82.%D1%80%D1%84

Check out what URLs you will get after going through this link in your application.

My production solution. Method "change language" redirects to any page with proper locale just using HTTP_REFERER in request object. Please note: URI.path method for get only path, not whole url

Make "change language" method in any controller:

        def change_lang

        if request.referer.nil?
                 refer = root_url
        else
                 uri = URI(request.referer)
                 refer = uri.path
        end
        lang = params[:lang]
        cookies[:locale] = lang
        redirect_to refer

        end

application_controller.rb

before_action :set_locale

def set_locale

# -- Get lang from cookies or url parameter locale

user_locale = cookies[:locale] || params[:locale]

# -- If present

if user_locale.present? 

    # -- If it is has 2 symbols

    user_locale = user_locale.scan(/[a-zA-Z]{2}/) 
else

    # -- If no - use default en locale

    user_locale = 'en'
end


# -- Check, is this locale available for using.
# Please note: this needed for disable invalid locale warning.

if I18n.available_locales.include?(user_locale[0].to_sym)

    I18n.locale =  user_locale[0]
else
    I18n.locale =   "en"
end

end

add this to your layout

<%= link_to 'English', change_lang_path('en') %> <%= link_to 'Russian', change_lang_path('ru') %>

config/routes.rb

scope "(:locale)", locale: /[a-zA-Z]{2}/ do
get "change_lang/:lang" => "users#change_lang", :as => "change_lang"
end

There is no need to use params.merge or any monkey-patch solution.

I hope this helps, because I personally spent a lot of time to solve it.


So I found a way to more explicitly do this with out relying on (as much) rails magic.

url_for(params.merge({:your_new_parameter => value}))

This should work in any link_to.

All its doing is taking the current request's parameters and merging your new desired hash into them and then creating a new url for that.


Took me a while to find this but here is my solution:

link_to 'English', url_for( :locale => 'en' )
link_to 'Deutch', url_for( :locale => 'de' ) 

From the docs here: http://api.rubyonrails.org/classes/ActionController/Base.html#M000649

When generating a new URL, missing values may be filled in from the current request‘s parameters. For example, url_for :action => ‘some_action‘ will retain the current controller, as expected. This behavior extends to other parameters, including :controller, :id, and any other parameters that are placed into a Route‘s path.

So using url_for will default to the current request's parameters, just change the one's you want in your code. In this case all I changed was :locale, so everything else stays the same.

Note this also works for "hidden" :parameters. So if you have:

map.my_map ':locale/my_map', :controller => 'home', :action => 'my_map'

using the above url_for in the page /en/my_map will not have 'home' in the url (ie /en/home/my_map). Bonus.