Creating routes with an optional path prefix
-
03-07-2019 - |
Question
How can I go about making my routes recognise an optional prefix parameter as follows:
/*lang/controller/id
In that the lang part is optional, and has a default value if it's not specified in the URL:
/en/posts/1 => lang = en
/fr/posts/1 => lang = fr
/posts/1 => lang = en
EDIT
Ideally, I'm looking to do this across many controllers and actions by mapping a namespace:
map.namespace "*lang" do |lang|
lang.resources :posts
lang.resources :stories
end
Solution
OK, I've managed to sort out this problem:
THere is no way of doing this in Rails by default (at least, not yet). Instead of using namespaces and default values, I needed to install Sven Fuchs' routing filter.
Once the plugin is installed, I added the following file to my lib directory:
require 'routing_filter/base'
module RoutingFilter
class Locale < Base
# remove the locale from the beginning of the path, pass the path
# to the given block and set it to the resulting params hash
def around_recognize(path, env, &block)
locale = nil
path.sub! %r(^/([a-zA-Z]{2})(?=/|$)) do locale = $1; '' end
returning yield do |params|
params[:locale] = locale || 'en'
end
end
def around_generate(*args, &block)
locale = args.extract_options!.delete(:locale) || 'en'
returning yield do |result|
if locale != 'en'
result.sub!(%r(^(http.?://[^/]*)?(.*))){ "#{$1}/#{locale}#{$2}" }
end
end
end
end
end
I added this line to routes.rb:
map.filter 'locale'
This basically fills out a before and after hook, generated by the plugin, that wraps the rails routing.
When a url is recognised, and before Rails gets to do anything with it, the around_recognize method is called. This will extract a two-letter code representing the locale, and pass it through in the params, defaulting to 'en' if no locale is specified.
Likewise, when a url is generated, the locale parameter will be pushed into the URL on the left side.
This gives me the following urls and mappings:
/ => :locale => 'en'
/en => :locale => 'en'
/fr => :locale => 'fr'
All existing url helpers work as before, with the only difference being that unless the locale is specified, it is preserved:
home_path => /
home_path(:locale => 'en') => /
home_path(:locale => 'fr') => /fr
OTHER TIPS
You can define defaults in a route using the :defaults
key. Try this:
map.connect ':lang/posts/:id', :controller => 'posts', :action => 'show',
:defaults => { :lang => 'en' }
I understand that this old question, but it may be useful.
map.with_options(:path_prefix => ":locale") do |m|
m.resources :posts
m.resources :stories
end
And you have to add before filter to application controller to define locale, such as
before_filter :define_locale
def define_locale
if params[:locale] == nil
I18n.locale = 'en'
else
I18n.locale = params[:locale]
end
end
I'm guessing (no time to test right now) that this might work:
map.connect ':language/posts/:id', :controller => 'posts', :action => 'show'
map.connect 'posts/:id', :controller => 'posts', :action => 'show'
OK, tried it - it works, on Rails 2.1.1 at least. So that's good. Can't get the :defaults idea to work, though, which is a shame, because it's DRYer.
Thought you could at one time use a [key] => nil to specify an optional parameter. Something like:
map.connect ':lang/posts/:id', :controller => 'posts', :action => 'show', :lang => nil