Concise, localized Rails URL helpers? Solved (twice).

posted: May 13th, 2007 · by: Sven

in: Globalization, Programming · tagged as: , , , , , ·  25 comments »

Originally triggered by Jeremy Hubert I’ve posted some thoughts about more concise and transparently localized Rails url_helper methods last month.

Basically Jeremy nailed a problem that occurs as soon as you define a Rails route that includes the locale as the leftmost parameter: you now can’t use Rails’ url_helper methods in a reasonable DRY way any more! A reasonable solution was pending.

Plugins to the rescue. By the end of this article you’ll be able to choose between two different solutions to this problem.

The Problem

For example you might have defined a route like this:

map.connect ':locale/:controller/:action/:id'

Or with Rails’ RESTy resources:

map.resources :articles, :path_prefix => ":locale"

How would you now use url_helper methods without specifying the locale every single time you’re calling an url_helper? You can’t. Without any further efforts you’d have to say, e.g.:

article_path(@current_locale, @article)

instead of being able to just use:

article_path(@article)

And the latter clearly is what one would expect from a Rails solution, isn’t it? It is.

The solutions

There are two different solutions to this problem (that I know of, of course):

With respect to the problem at hand and their solution to it they both have quite something in common: Both of them achieve largely the same, particulary both provide a reasonable flexibility concerning the Rails url_helpers that triggered this quest. Both of the solutions are available as plugins and are installed super-easily. Both are quite young, too.

On the other hand they both take a pretty different route to solve our problem and thus, there’s a series of difference too, of course. Let’s see …

The resource_fu plugin

I hope that I get things right when I say that ressource_fu basically does two things and thereby solves our original problem:

  • Most importantly it tries to “guess” (or “infer”) parameters that you haven’t passed to your url_helper, but that are necessary to build the URL. E.g., when you ommit the :locale parameter, it looks for a (controller) instance variable with the same name.
  • Secondly it “anchors” positioned arguments on the right side. That means that if your route has three segments and you only supply two positional arguments, the url helper assumes you are supplying the *last* two segments.

This way both of these will work as long as you have defined a controller instance variable @locale (from which the parameter will be “inferred”):


article_path(@article) # positioned arguments
article_path(:id => @article) # verbose hash syntax 

Hurray. Mission complete!

Resource_fu has more benefits than this. Read more about it in its readme file. You can download/install the resource_fu plugin from here:

http://svn.protocool.com/public/plugins/resource_fu/

The localized_url_helper plugin

The localized_url_helper plugin tackles things seperatly and targeted:

  • For the hash argument syntax it will smuggle the locale past the Rails routes’ “parameter expiry” mechanism. It thereby tricks Rails into simply using the locale parameter and then proceeding as if it wasn’t there at all.
  • For the positioned argument syntax it’s sufficient to just push the locale onto the front of the arguments list.

Two very simple steps and that’s it.

You can download/install the localized_url_helper plugin from here:

http://svn.artweb-design.de/stuff/rails/localized_url_helpers/

Which solution should you choose?

I really don’t know :)

Both plugins achieve largely the same thing with respect to this problem but they are using two quite different approaches. Even at this microscopic scale there’s no silver bullet, I guess. Look at the differences.

Resource_fu does more than just solving the original problem - and it is designed to do so. The main selling point of resource_fu is: it changes the way url helpers behave. In my personal opinion it does this in a very elegant, reasonable and useful way. Check it out! If that’s what you want (and if you’re probably starting a new project so don’t have to double-check all your url_helpers) resource_fu definitely is for you.

Localized_url_helper on the other hand is probably more of the laser-scalpel type of tool. It solves exactly the problem posed above and nothing else. Also it’s quite a bit less code and it’s coded as unobtrusive and careful as possible (as far as I can tell, that is). So it probably is a bit less prone to breakage through changes in future Rails updates.

leisure time on beach: rails localized url helper solved :-)

Actually I learned about resource_fu after I’ve presented the first version of my plugin to the Globalize developers list.

I probably wouldn’t have started trying to come up with my own plugin at all if I would have known about Trevor’s resource_fu in the first place. But digging through the Rails routing code and trying to patch it to behave the way I wanted has been pretty instructive. I can only recommend that!

Anyways I think the results have been worth it: at the end of the day we can now choose between to solutions when it comes to localize our routes and keep our URL helpers DRY at the same time. :-)

Feedback?

What do you think?

Leave a comment

25 Comments

  1. Morgan Roderick said April 26th, 2008 at 07:28 PM  

    It’s certainly a few good steps in the right direction, but it won’t handle old-school urls, created using :controller => ‘mycontroller’, :action => ‘myaction’ syntax, as they don’t use the fancy url helpers.

    I’ve been trying to get my head around this all day, to no avail.

    The two solutions above certainly helps quite a lot, but won’t solve all the issues with having language as a magic first-part of your paths. Try using some of the testing plugins, and you’ll see what I mean ;-)

    Keep digging!

  2. Stefano Grioni said July 3rd, 2008 at 12:11 AM  

    I tried your Localizedurlhelper which helped me a lot. Great work. I just had a problem because I didn’t find any way (based on your helper) to set a default language for the website. And so when /foo was called, it complained that the route was defined for /locale/foo I figured out a little (and pretty nasty) workaround (If anyone has got anything better, I’ll be glad to change my code :) ) :

    in routes.rb

    instead of just putting

    map.resources :controller, :path_prefix => ‘:locale’

    I added map.resources :controller

    That’s why I say it’s nasty .. Because you have 2 routes for 1 real url.

    And then in application.rb

    beforefilter :setlocale def set_locale default_locale = ‘en-US’ requestlanguage = request.env[‘HTTPACCEPT_LANGUAGE’] requestlanguage = requestlanguage.nil? ? nil :request_language[/[^,;]+/]

    @locale = params[:locale] || session[:locale] ||
    request_language || default_locale
    session[:locale] = @locale
    begin
      Locale.set @locale
    rescue
      @locale = default_locale
      Locale.set @locale
    end
    params[:locale] ||= @locale
    

    end

    Hope it helps..

    Anyway, thanks again for all your work

  3. Mattias Ottosson said September 11th, 2008 at 03:08 PM  

    Hi Sven. Thanks for your articles on globalize, they really helped me to get going with globalize. I’ve encountered a problem though and i’ve been banging my head in the wall the last two days cause I can’t come up with a decent working solution two rest routes were the urls are in two different languages.

    Let’s save a product model/products controller and you want to have a rested route to this with a lang-prefix. It’s no problem with the soloution above and having map.resources :products, :pathprefix => ‘:locale’ and simply calling the productspath. You get for example /en/products and /sv/products.

    Now to the problematic part. I want to localize the controller-name of the route => /en/products when the locale is set to english and /sv/produkter when the locale is set to swedish.

    Is this possible?

    Any help will be greatly appreciated!

  4. Babarocap said October 22nd, 2008 at 04:51 PM  

    Mattias Ottosson : Now to the problematic part. I want to localize the controller-name >of the route => /en/products when the locale is set to english and >/sv/produkter when the locale is set to swedish.

    Try this plugin : http://github.com/raul/translate_routes/tree/master

  5. Pawel Barcik said August 7th, 2009 at 01:07 PM  

    this looks like a better solution (simple flexible wrapper)

    http://github.com/svenfuchs/routing-filter/tree/master

  6. val web said August 16th, 2009 at 01:06 AM  

    Hi Sven,

    Thank you for the routing-filter’s code.

    Could you please show how to use it in a project?

    Thanks.

  7. Sven said August 17th, 2009 at 09:31 AM  

    Hi Val,

    basically, you just tell your routes to use certain filters using “map.filter :whatever”.

    You could have a look at adva-cms http://github.com/svenfuchs/advacms/blob/50a9aae6e4d72d30cdf3d16f7ac1606c55e96fa1/engines/advacms/config/routes.rb

    Of course you might need different implementations of filters or entirely different filters.

  8. val said August 17th, 2009 at 03:46 PM  

    Thanks Sven,

    I’m trying to auto-switch between locales by setting I18n.locale based on the url, that is http://example.com/en/post would switch to English. So I added to app’s controller a before_filter. Is there something like that in routing-filter?

  9. val said August 17th, 2009 at 03:56 PM  

    Anyway I did like in the example you gave (in base_controller.rb)

    def set_locale params[:locale] =~ /^[\w]{2}$/ or raise ‘invalid locale’ if params[:locale] I18n.locale = params[:locale] || I18n.default_locale # TODO raise something more meaningful I18n.locale.untaint end

    Just instead- I18n.locale = params[:locale] || I18n.default_locale I converted it to symbol I18n.locale = params[:locale].tosym || I18n.defaultlocale since it didn’t work the original way, and now my app is happy :)

    Thanks again!

  10. Frank said November 20th, 2009 at 10:39 PM  

    Hey guys,

    A great solution I found to this problem is this one : http://stackoverflow.com/questions/212425/creating-routes-with-an-optional-path-prefix

  11. Chris said May 12th, 2010 at 02:42 PM  

    Hi, this looks like a better solution (simple flexible wrapper)

    http://github.com/svenfuchs/routing-filter/tree/master

  12. Funny said May 22nd, 2010 at 07:35 PM  

    Hi, I’m trying to autoswitch between locales by setting I18n.locale based on the url, that is http://example.com/en/post would switch to English. I added to app’s controller a before_filter. Is there something like that in routing-filter?

  13. Private accommodation in Tenerife said September 14th, 2010 at 02:29 PM  

    Welcome I recommend you holidays in Tenerife. It is a beautiful place, ideal for a dream holiday after work. If you miss the chance, I recommend the apartments in Tenerife from Canartours. This is a very good company which can boast competitive prices. Welcome.

  14. apartments at tenerife said December 9th, 2010 at 11:20 AM  

    interesting read that article, really interesting.

  15. jack said January 23rd, 2011 at 11:27 AM  

    thanks for that heads up. That’s a useful tip! I’ve never ran into that, but for sure that’s something quite some people will need a solution for. cheap vps

  16. Vacuum Pump 4U said February 19th, 2011 at 07:11 AM  

    Thanks for the info on URL helpers here! Very useful as always and what a great resource Artweb Design is. Keep up the great work.

  17. Lisa said February 26th, 2011 at 01:58 PM  

    Yeah thats a good solution. There was actually another way of doing it that I found in the technology section of a news site I read.

  18. chat said March 31st, 2011 at 07:26 PM  

    Here is the localized rails fix:

    Dependencies.loadoncepaths -= Dependencies.loadoncepaths.select{|path| \ path =~ %r(^#{File.dirname(FILE)}) }

  19. bamesk said April 10th, 2011 at 07:27 AM  

    Well I definitely liked reading it. This information provided by you is very constructive for correct planning. blendtec total blender | delonghi toaster oven | breville toaster oven

  20. Stefan said April 12th, 2011 at 07:03 PM  

    Hi Sven danke dir für die Infos, wir hatten ein paar URL Probleme mit unserer singlereisen test seite. Werde es mal mit deinen Tipps probieren und mal schauen ob es der singleurlaub Seite weiterhilft. Danke dir, Stefan.

  21. breville toaster oven said April 18th, 2011 at 02:39 PM  

    The resource_fu plugin solved my issue too. It is absolutely awesome.

  22. side sleeper pillow said April 22nd, 2011 at 07:10 AM  

    Nicely written article, Knowledgeable and informative post. I’m really glad I came my way along your site. Keep posting, I really like the whole topic. Thanks for sharing.

  23. Okey oyunu said May 12th, 2011 at 03:58 PM  

    Thanks.

    Tüm dünya artik okey oyunu oynuyor. Yillardir bir çok oyun programi olmasina ragmen, içlerinden en güzeli olarak nitelendirebilecegimiz tek bir site göze çarpmaktadir. Diger tüm okey oyunu programlarinin aksine ücretsiz olmasi ve 3 boyutlu olarak hizmet vermesi mükemmel bir gelismedir. Sizlerde www.okey-oyunu.com adresinden bu essiz okey oyununu indirebilirsiniz. Kullanimi çok basit ve Türkçe dil seçenegi ile kolaylikla oyuna baslayabilirsiniz. Ister kendi ülkenizden, isterseniz dünyanin tüm farkli bölgelerinden dilediginiz oyun odalarini seçerek, oyuna hemen baslayabilirsiniz. Okey oyunu oynamak için artik arkadas bile aramaniza gerek kalmadan, bilgisayarinizdan 100 binlerce üye ile online olarak okey oyununu oynamanin zevkine varabilirsiniz.

  24. porno said May 22nd, 2011 at 01:31 PM  

    I do agree with all of the ideas you have presented in your post. They’re really convincing and will definitely work. Still, the posts are too short for newbies. Could you please extend them a bit from next time? Thanks for the post.

  25. porno said May 22nd, 2011 at 02:07 PM  

    good comment. thanks you friends.

    I’ve surfed the net more than three hours today, however, I haven’t found such useful information. Thanks a lot, it is really useful to me

Sorry, comments are closed for this article.

artweb design
Sven Fuchs
Grünberger Str. 65
10245 Berlin, Germany


http://www.artweb-design.de

Fon +49 (30) 47 98 69 96
Fax +49 (30) 47 98 69 97