How to set up your routes? - Get on Rails with Globalize! Part 4 of 8
posted: March 17th, 2007 · by: Sven
Routes are one of the harder-to-grasp areas of Rails. How should you set up your routes for Globalize? Do you need to change anything at all?
As often, the short answer is: it depends. It might well be that you can leave your routes setup completely untouched. Probably they’ll need some easy touches though.
This article tries to provide a (slightly) longer answer and shows some of the basic options you have. (There has been quite some request for a howto on this topic, so I’ve decided to change the original outline of this series).
When you’re starting to Globalize your application one essential design question is: how to incorporate the different locales that you’re going to support into your application.
You might store your users locale in a session to persist it between subsequent requests. You might determine it from the accept-language header or even geolocate the users IP address.
In these cases you probably don’t need to touch your routes setup at all. You just stuff the users locale into the session, retrieve it from there (or somewhere else) on every request and … are done: your URLs and therefor your routes remain the same.
If you’re going to go with a more RESTful approach though where the locale is designed to be an essential part of the URL some simple extra work might be necessary.
Storing the locale in the URL …
There are basically two options. You can keep the locale as:
- a parameter (like in http://site.com/articles?locale=fr)
- a part of the path (like in http://site.com/fr/articles)
(Of course you can also encode the locale as a part of the domain name, too. That’s basically the same option like putting it in the path. Rails’ support is somewhat limited here, though.)
Now, in case 1. you don’t necessarily need to change your routes setup, too.
… as a parameter
Putting the locale as a query-string parameter into the URL could look like this:
http://site.com/articles/index?locale=fr
E.g. let’s say you have scaffolded an ArticlesController, Article model and the usual suspects of views. Now you can easily use the (anonymous) Rails default route:
map.connect ':controller/:action/:id'
… you just need to make sure that in your views the locale parameter is added to your URLs everywhere, like in:
<%= link_to 'Index', url_for(:action => :index, :locale => params[:locale]) %>
That looks a little cluttered, doesn’t it? The explicit keys and the specification of the locale both seem to be necessary to append the locale parameter to the URL though.
You can also use named routes like they are automatically set up for those shiny new resources in Rails 1.2 (of course you could to set them up manually instead):
map.resources :articles
… and then use the more concise
<%= link_to 'Index', articles_path(:locale => params[:locale]) %>
Why not?
Some people don’t like to append information like this to the actual URL as a parameter in this manner though because they think it just looks ugly.
From a more RESTful perspective the basic difference is whether or not you see the locale to designate different resources. I.e. you might see your - say - three available translations of a certain article as different representations of the same resource. In this case, using a request parameter would adequately express this perception. Or you might see the available translations more as discrete resources that have multiple views on their own. In this case, using the locale as part of the path or domainname would be more appropriate.
One very practical disadvantage of this approach is that it won’t work well with Rails’ build-in page and action caching as these don’t respect URL parameters (i.e. without further efforts Rails wouldn’t cache different versions of pages for different locales).
… as a part of the path
So depending on your application and preferences you probably want to move the locale to the beginning of the URL path like in http://site.com/fr/articles. As you already guessed, this time you’ll definitely have to adjust your routes setup accordingly.
This one would cut it with a classic, unnamed route:
map.connect ':locale/:controller/:action/:id'
Yet, this time you don’t need to specify the locale for url_for: the Rails routing mechanism already knows that a parameter named :locale is needed and will find it in the controller’s parameters (where you’ve set it within your ApplicationController#before_filter).
So, the following will work:
<%= link_to 'Index', url_for(:action => :index) %>
Again, you can use Rails 1.2 resources with this approach, too, of course. This time, you’d need to append the special :path_prefix option to the resouce definition:
map.resources :articles, :path_prefix => ':locale'
And boom! … that’s it. You’re ready to go with fully globalized RESTful resources mappings.
Depending on your usage of url_for or the autogenerated [model]_path methods you might need to adjust these a little bit though. For example, the following quite concise usage works perfectly without a locale on the path:
# in routes.rb:
map.ressources articles
# in your views:
<%= link_to "Show", article_path(@article) %>
But with a resource routes setup using :path_prefix like above, this will throw the error:
article_url failed to generate from {:action=>"show", :locale=>"1",
:controller=>"articles"}, expected: {:action=>"show", :controller=>"articles"},
diff: {:locale=>"1"}
This seems to indicate that the article instance has been used to fill in the :locale parameter instead of :id. So we’d need to complete this as follows:
# in routes.rb:
map.ressources articles, :path_prefix => ":locale"
# in your views:
<%= link_to "Show", article_path(:id => @article) %>
So there’s quite a handful of options.
If you ask me the latter one is the one with the most advantages in most cases (as always, mileages vary):
- better compling with REST (almost always a big plus)
- using named routes (less extra work and better decoupled code)
- being friendly to Rails caching mechanisms (in case you’re going to use them)
Boom. That’s it :-)
What do you think? Does your routes setup follow a different approach? If there are additional options to add, please let me know.
Next time I’ll (hopefully) better respect the original outline of this series again and come up with the scheduled “Hints, tips and tricks” collection installment. If there’s something useful … a tip, trick, technique, … that you think would make a useful addition to that installment: please use the comments or send me an email!
Cyx said March 17th, 2007 at 05:41 AM ¶
The example regarding putting it in the path works well when you use
articlepath(:id => articleid)
but if you try using
articlepath(articleid)
this will throw an error because Route expects the first param to be the locale
so it should be
articlepath(‘en’, articleid)
for example.
Currently in our proj, we hacked the routes in order to assume the first locale param for all routes to be the same ones as params[:locale], but we’re keeping a close eye on the hack, as it might break in any future rails commit.
Sven said March 17th, 2007 at 01:37 PM ¶
Yes, that’s true.
Cyx, I’m highly interested in seeing the “hack” you’ve applied to the routes as I’ve been thinking about something like this myself. Is it published somewhere? Or could you send it to me by email?
Jeremy Hubert said April 7th, 2007 at 02:58 AM ¶
Hey Sven,
Great work on the tutorial.
I really like the idea of having the locale in my url, but passing it into the helper would be a pain in the butt and would pretty much remove the transparency of Globalize (which is something that I love)
So.. I hacked this together and it seems to work pretty well. I haven’t used it in production yet, but it works on my local system with Rails 1.2 installed.
http://pastie.caboo.se/52246
Check it out and let me know what you think.
Cheers, Jeremy
Sven said April 7th, 2007 at 03:17 PM ¶
Hey Jeremy,
your route helper patch looks super-interesting! Actually I had scheduled to wrap my head around this matter, too, but you know what’s happening with such todo items sometimes …
I’ll have a closer look at this asap - probably not before the end of next week though. Please keep me posted on any news about this!
Bolo said April 19th, 2007 at 01:27 AM ¶
You don’t have no problem with formfor(:enseignant, :url => enseignantspath, :html => { :multipart => true })
i have a Routing Error
enseignant_url failed to generate from
?
Sven said April 22nd, 2007 at 02:16 AM ¶
Sorry, Bolo, I seem to have overlooked your comment. I just haven't tried that, yet. I'll catch up on that asap. Looking at your code though my first bet would be that you're encountering the same problem that I've described with my article_path example. So you'd probably need to add some paramters to enseignants_path?dewdrops said July 3rd, 2007 at 11:46 AM ¶
hi, I have a problem in named routs this is what i have declared in the routes.rb :
map.profileview ‘:name/:id’, :controller=> “user”, :action=> “viewprofile”, :id => /\d+/ , :name => /.*/
the url formed looks like this on localhost:
http://localhost:3000/User_Name/10
what i want: http://localhost:3000/User_Name
Any way to hide this id that I send in the url ?
thnx
Jarkko Laine said August 31st, 2007 at 10:29 AM ¶
@dewdrops:
Remove the :id parameter from the url string. You have to make sure then, though, that the name attribute is unique so that the url will always be unique as well.
Taylor Swift said January 13th, 2008 at 09:21 PM ¶
I’m getting error with: articlepath(articleid) Could anyone help me?
Sven said January 15th, 2008 at 02:08 PM ¶
Hi Taylor,
you’d need to be bit more specific with that question … nobody could possibly answer it this way :)
Jake said January 25th, 2008 at 12:30 PM ¶
Is it possible to customize content for each locale with Globalize?
For example, a real estate site would have different cities listed for their US version then their UK.
In other words, is it possible to localize content in the “views” besides the currency, times, dates, etc. ?
Thanks.
Sven said January 25th, 2008 at 02:02 PM ¶
Hi Jake,
yes it is. Actually this (i.e. model data translation) is one of the “unique selling points” of Globalize compared to most other Rails i18n solutions.
You might want to refer to this for details: http://www.artweb-design.de/2006/11/10/get-on-rails-with-globalize-comprehensive-writeup
Jake said January 26th, 2008 at 04:44 PM ¶
Sven,
Thanks for your reply!
jujudellago said January 28th, 2008 at 12:55 PM ¶
Hello,
I love globalize, but I had a really hard time implementing appropriate routes to include the locale in the path.
Everything went well simply keeping the locale in the session to display my pages, but I wasn’t familiar with caching yet, and got so disappointed when my performances were great, but the list of articles always came up in the same language once cached.
So I tried the various examples, I thought the best would be as “Jeremy Hubert” mentioned, but I just couldn’t get it to work to automatically add the locale to all my REST routes.
After that I choose to add the locale to all my routes, with the :path_prefix, adding the
:locale => params[:locale]in all my links (what a pain)but the main problem was that I don’t need to localize all my models, for most of them it’s pointless, so it became a real nightmare to make sure they’ll always have something in the params….
I don’t know how I didnt think of that earlier, but as my “set_locale” method defines the locale in the session, I changed my links to the controllers that needed localization to have something like:
articles_path(:locale => Locale.language.code )so if the link is displayed from a controller that didn’t have the locale as a parameter, it always works !!
I don’t know if what I’m saying makes sense, but I think my project will be just fine now, I only changed the routes for the controllers that needed it, adding the locale param when I needed a cache
btw, I’m using the “content_cache” plugin to cache the content of my pages and keep the user specific sections, it needed some bug fix described in the comments of the articles to work with rails >1.2.3 , but it works great with globalize ! http://blog.codahale.com/2006/04/10/content-only-caching-for-rails/
Sven said January 28th, 2008 at 03:24 PM ¶
Hi jujudellago!
Thanks for the hints. These are quite useful :)
Also, have you seen this article about “Concise, localized Rails URL helpers”? http://www.artweb-design.de/2007/5/13/concise-localized-rails-url-helpers-solved-twice
I guess that might solve your issues with spelling out the locale param all the time, too.
jujudellago said February 6th, 2008 at 03:20 AM ¶
Wow !
How did I miss that one !! I reverted my code, and picked the option with ressource_fu because it also helped me to solve other routing issues…
I guess I could have save a lot of time if I had read more carefully your (excellent !!) blog, but I learned a lot in the process anyway.
thanks a lot for everything you put on your blog, it’s a life saving resource for a rails beginner ! (well now my apps are getting better and better but I still feel like a beginner somedays ;) )
cheers
Andy Watts said February 27th, 2008 at 07:37 PM ¶
Many thanks for this.
pimpmaster said May 2nd, 2008 at 11:09 AM ¶
Great write-up!
I been scouring the net for something like this and your post gets me 90% to where I want to be. My only question is:
How can I make my routes translate as well?
www.mysite.com/en/articles www.mysite.com/en/articles/new www.mysite.com/en/articles/english-permalink/edit
www.mysite.com/es/articulos www.mysite.com/es/articulos/nuevo www.mysite.com/es/articulos/spanish-permalink/editar
Dan said May 7th, 2008 at 12:29 AM ¶
Hi Sven,
Thanks for the article - I love the idea of having the locale in the URL however I am having a problem with the form_for method.
5: <% form_for(@order) do |f| %>
My error message: order_url failed to generate from {:action=>”show”, :lang=>#<order>, :controller=>”orders”}, expected: {:action=>”show”, :controller=>”orders”}, diff: {:lang=>#<order>}
obviously the order is being passed to the :lang attribute instead of to the :order attribute, but since I am not calling the url_for method I don’t know what to do. I’m calling the form_for method which is calling the url_for.
I tried Jeremy’s environment.rb hack above but it didn’t work for me (perhaps because i’m running rails 2.02).
Any ideas?
Sven said May 11th, 2008 at 05:52 PM ¶
Hi pimpmaster,
that’s not possible with Globalize itself. Saimon has a plugin for that though: localized_urls
Hi Dan,
have you checked the solutions for localized url_helpers discussed here? I believe this should work.
If it doesn’t you might get around that by explicitely setting the URL for your form_for call like form_for @order, :url => order_path(@order).
hth :)
jack said January 23rd, 2011 at 11:43 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
Allenwood said February 1st, 2011 at 12:07 PM ¶
mens gadgets - At World Of Gadgets we are continually updating our range of exciting new gadgets gizmos and novelty clothing/items to accomodate all tastes.
QQQ said February 7th, 2011 at 06:30 PM ¶
Finally we kissed and the passion scale went sky high and I knew I was onto a good thing - sex was a certainty free porn videos. She never hesitated when I began to fondle her breasts and she willingly exposed them for me mobile porn. They were firm and I suspected a breast enhancement but said nothing - they still felt good and I was enjoying them and gradually working my way further south free porn tube. She was a step ahead of me and before I could completely undress her she moved on me atk hairy and I was suddenly having my pants pulled down and I was enjoying one of he best cock sucking hairy pussy experiences I had ever had. ABB728019394
Yeng2 said February 24th, 2011 at 06:39 AM ¶
Ok ample this out. I was aggravating to install it in the sub directory of area I capital the app to be and it should be installed in the home directory. pass4sure mb2-631 over & out, Aggravating to get things bureaucracy myself but accepting a little trouble. pass4sure mb6-819 Anywhere I should attending to for help, pass4sure 70-432 I approved but I accumulate getting forward about the block by support.
muneeb said February 24th, 2011 at 01:42 PM ¶
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.Go Ped
Josie said March 2nd, 2011 at 01:59 PM ¶
Yeah this was talked about on a business article directory and I came to a similar conclusion.
tattoo sales said March 21st, 2011 at 09:40 AM ¶
It was a beneficial workout for me to go through your webpage. It definitely stretches the limits with the mind when you go through very good info and make an effort to interpret it properly
chat said March 31st, 2011 at 07:50 PM ¶
The following cleaned up the issue during the setup for rails:
Dependencies.loadoncepaths -= Dependencies.loadoncepaths.select{|path| \ path =~ %r(^#{File.dirname(FILE)}) }
Stefan said April 12th, 2011 at 07:07 PM ¶
Schöne Sache, werde das mit den Routes mal für die sprachreisen vergleich seite ausprobieren an der ich grad sitze. Glaube, dass diese Ansätze einige Probleme lösen sollte. Werd mal schauen und melde mich im Zweifel nochmal.
side sleeper pillow said April 22nd, 2011 at 04:29 AM ¶
Sehr gehr zu ihr gessen!
Okey oyunu said May 12th, 2011 at 04:08 PM ¶
Thanks for this article. 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.
porno said May 22nd, 2011 at 01:28 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.
porno said May 22nd, 2011 at 01:59 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