Storing the locale in the URL without having to pass it into resource helpers
Goal: article_path(@article) #=> /en/article/1
Overriding define_url_helper
All I did was add one line to this method. I have placed this code in my environment.rb file. I know there is a cleaner way, but I'm just testing this for now. This should get some ideas going.
module ActionController
module Routing
class RouteSet
class NamedRouteCollection
def define_url_helper(route, name, kind, options)
selector = url_helper_name(name, kind)
# The segment keys used for positional paramters
segment_keys = route.segments.collect do |segment|
segment.key if segment.respond_to? :key
end.compact
hash_access_method = hash_access_name(name, kind)
@module.send :module_eval, <<-end_eval # We use module_eval to avoid leaks
def #{selector}(*args)
!! #{'args << { :locale => @current_locale }' if segment_keys.include?(:locale)}
opts = if args.empty? || Hash === args.first
args.first || {}
else
args.zip(#{segment_keys.inspect}).inject({}) do |h, (v, k)|
h[k] = v
h
end
end
url_for(#{hash_access_method}(opts))
end
end_eval
@module.send(:protected, selector)
helpers << selector
end
end
end
end
end
The Application Controller - Setting the @current_locale
map.index ':locale', :controller => 'general', :locale => 'en-US'
map.hello ':locale/admin', :controller => 'general', :action => 'admin'
map.connect 'test', :controller => 'general', :action => 'test'
map.resources :features
map.resources :articles, :path_prefix => ":locale"
# Install the default route as the lowest priority.
map.connect ':locale/:controller/:action/:id.:format'
map.connect ':locale/:controller/:action/:id'
# You can also define another set of default routes for when the locale is not in the params
map.connect ':controller/:action/:id.:format'
map.connect ':controller/:action/:id'
The Result
Assuming params[:locale] = 'en' and params[:controller] => 'general'
link_to 'Articles', articles_path #=> /en/articles
link_to 'Articles', articles_path(:locale => 'fr') #=> /fr/articles
link_to 'Features', features_path #=> /features
link_to 'Home', index_url #=> /
link_to 'Home', index_url(:locale => 'fr') #=> /fr
link_to 'Test', :action => 'test' #=> /en/general/test
# The only caveat that I've found so far is that you need to specify the controller when trying to change the locale without a named route
link_to 'Always French', :controller => 'general', :action => 'french', :locale => 'fr'
# This returns an error that I haven't had time to investigate. Be my guest. :)
link_to 'Always French', :action => 'french', :locale => 'fr'