doubledrones-i18n_routing 0.3.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,55 @@
1
+ 0.3.6 (June 13, 2010)
2
+
3
+ * Works even if translations are the same as the translated name
4
+
5
+ 0.3.5 (June 9, 2010)
6
+
7
+ * Clean up some .to_sym in order to avoid unnecessary symbols during runtime
8
+ * Works with both I18n.locale as string or as symbol
9
+ * Try to fix a Pathname instead of string issue
10
+
11
+ 0.3.4 (June 3, 2010)
12
+
13
+ * Fix Rails3 issue with nested single resource again and pluralize controller name as it should be
14
+
15
+ 0.3.3 (June 2, 2010)
16
+
17
+ * Fix Rails3 issue with nested single resource
18
+
19
+ 0.3.2 (June 2, 2010)
20
+
21
+ * Add automatically config/locales/*.yml in the i18n load path in order to fix Rails3 issue
22
+
23
+ 0.3.1 (May 27, 2010)
24
+
25
+ * Add a small Rails3 verification
26
+
27
+ 0.3.0 (May 27, 2010)
28
+
29
+ * Add support for translating path_names, member and collection
30
+ * Fix Rails3 nested resources
31
+ * All specs are now passing under both Rails 2.3.5 and Rails 3
32
+
33
+ 0.2.4 (Apr 21, 2010)
34
+
35
+ * Fix latest Rails3 edge compatibility (previous rails3 beta version not supported any more)
36
+
37
+ 0.2.3 (Apr 21, 2010)
38
+
39
+ * Works on ruby 1.9.2-head
40
+
41
+ 0.2.2 (Apr 21, 2010)
42
+
43
+ * Add I18n gem dependency
44
+
45
+ 0.2.1 (Apr 09, 2010)
46
+
47
+ * Minor Rails3 modification, added gemspec and pushed gem on gemcutter
48
+
49
+ 0.2.0 (Apr 07, 2010)
50
+
51
+ * All specs pass for Rails2, including nested deep resources
52
+
53
+ 0.1.0 (Mar 22, 2010)
54
+
55
+ * Initial release
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Guillaume Luccisano - g-mai|: guillaume.luccisano
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,28 @@
1
+ = I18nRouting
2
+
3
+ I18n_routing is a plugin for Ruby on Rails that lets you easily translate your routes trough the I18n api included in Rails since version 2.2
4
+
5
+ All necessary informations are available on the wiki: http://wiki.github.com/kwi/i18n_routing
6
+
7
+ For any question, use the i18_routing google group: http://groups.google.com/group/i18n-routing
8
+
9
+ Works with Rails 2.3 and Rails3 beta4 !
10
+
11
+ == TODO for next releases (written the 9th of June)
12
+
13
+ * Handle multiple translations for same resources name (Example: nested and not nested resources)
14
+ * Handle namespace translation
15
+
16
+ == I18n gem version warning
17
+
18
+ Be careful when running Rails 2.3 with the shipped i18n gem: this is an old i18n version and i18_routing will install the latest i18n version as a gem.
19
+ This latest version may be incompatible with some old usages.
20
+ Furthermore, if the i18n gem is present on your system, Rails will load it and skip the shipped version; Keep that in mind.
21
+
22
+ == Contributors
23
+
24
+ * kwi (Guillaume Luccisano)
25
+ * bigtiger (Jim Remsik)
26
+ * mixr (Reiner Dieterich)
27
+
28
+ Copyright (c) 2010 Guillaume Luccisano - g-mai|: guillaume.luccisano, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'spec/rake/spectask'
4
+
5
+ spec_files = Rake::FileList["spec/**/*_spec.rb"]
6
+
7
+
8
+ desc "Run specs for current Rails version"
9
+ Spec::Rake::SpecTask.new do |t|
10
+ t.spec_files = spec_files
11
+ t.spec_opts = lambda {
12
+ @rails_spec_version ? ["-c --format specdoc -- rails_spec_version=#{@rails_spec_version}"] : ["-c --format specdoc"]
13
+ }
14
+ end
15
+
16
+ task :default => :spec
17
+
18
+ desc "Run Rails 2.x specs"
19
+ task :rails2_spec do
20
+ @rails_spec_version = 2
21
+ Rake::Task['spec'].invoke
22
+ end
23
+
24
+ desc "Run Rails 3.x specs"
25
+ task :rails3_spec do
26
+ @rails_spec_version = 3
27
+ Rake::Task['spec'].invoke
28
+ end
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ # encoding: utf-8
2
+ require 'i18n_routing'
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+ require 'i18n_routing_common'
3
+
4
+ if Rails.version < '3'
5
+ require 'i18n_routing_rails2'
6
+ else
7
+ require 'i18n_routing_rails3'
8
+ end
@@ -0,0 +1,50 @@
1
+ # encoding: utf-8
2
+
3
+ # I18nRouting module for common usage methods
4
+ module I18nRouting
5
+ # Return the correct translation for given values
6
+ def self.translation_for(name, type = :resources, option = nil)
7
+
8
+ # First, if an option is given, try to get the translation in the routes scope
9
+ if option
10
+ default = "{option}Noi18nRoutingTranslation"
11
+ t = I18n.t(option, :scope => "routes.#{name}.#{type}", :default => default)
12
+ return (t == default ? nil : t)
13
+ else
14
+ default = "{name}Noi18nRoutingTranslation"
15
+
16
+ # Try to get the translation in routes namescope first
17
+ t = I18n.t(:as, :scope => "routes.#{name}", :default => default)
18
+
19
+ return t if t and t != default
20
+
21
+ t = I18n.t(name.to_s, :scope => type, :default => default)
22
+ return (t == default ? nil : t)
23
+ end
24
+ end
25
+
26
+ DefaultPathNames = [:new, :edit]
27
+ PathNamesKeys = [:path_names, :member, :collection]
28
+
29
+ # Return path names hash for given resource
30
+ def self.path_names(name, options)
31
+ h = (options[:path_names] || {}).dup
32
+
33
+ path_names = DefaultPathNames
34
+ PathNamesKeys.each do |v|
35
+ path_names += options[v].keys if options[v] and Hash === options[v]
36
+ end
37
+
38
+ path_names.each do |pn|
39
+ n = translation_for(name, :path_names, pn)
40
+ n = nil if n == pn.to_s
41
+ # Get default path_names in path_names scope if no path_names found
42
+ n ||= I18n.t(pn, :scope => :path_names, :default => name.to_s)
43
+
44
+ h[pn] = n if n and n != name.to_s
45
+ end
46
+
47
+ return h
48
+ end
49
+
50
+ end
@@ -0,0 +1,239 @@
1
+ # encoding: utf-8
2
+
3
+ #
4
+ # WARNING : Old and dirty Rails 2.x code
5
+ # Need clean up and intensive refactoring
6
+ #
7
+
8
+ module ActionController
9
+ module Routing
10
+ class Route #:nodoc:
11
+
12
+ alias_method :mkd_initialize, :initialize
13
+ def initialize(segments = [], requirements = {}, conditions = {})
14
+ @glang = requirements.delete(:glang)
15
+ mkd_initialize(segments, requirements, conditions)
16
+ end
17
+
18
+ private
19
+ alias_method :mkd_generation_requirements, :generation_requirements
20
+ # Add locale requirements to ensure good route is used when using url_for
21
+ def generation_requirements
22
+ r = mkd_generation_requirements
23
+ if @glang and !r.blank?
24
+ r << " and I18n.locale.to_sym == :#{@glang}"
25
+ end
26
+
27
+ return r
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ module ActionController
34
+ module Routing
35
+ class RouteSet #:nodoc:
36
+
37
+ attr :locales, true
38
+ attr :i18n_verbose, true
39
+
40
+ class Mapper
41
+ def localized(locales = I18n.available_locales, opts = {})
42
+ old_value = @set.locales
43
+ @set.locales = locales
44
+ @set.i18n_verbose ||= opts.delete(:verbose)
45
+ yield
46
+ ensure
47
+ @set.locales = old_value
48
+ end
49
+
50
+ end
51
+
52
+ class NamedRouteCollection #:nodoc:
53
+
54
+ alias_method :mkd_define_url_helper, :define_url_helper
55
+ def define_url_helper(route, name, kind, options)
56
+ gl = Thread.current[:globalized]
57
+
58
+ mkd_define_url_helper(route, name, kind, options)
59
+
60
+ if gl
61
+ selector = url_helper_name(name, kind)
62
+
63
+ rlang = if i = name.to_s.rindex("_#{gl}")
64
+ "#{selector.to_s[0, i]}_glang_#{gl}#{selector.to_s[i + "_#{gl}".size, selector.to_s.size]}"
65
+ elsif (gls = Thread.current[:globalized_s]) and i = name.to_s.rindex("_#{gls}")
66
+ "#{selector.to_s[0, i]}_glang_#{gls}#{selector.to_s[i + "_#{gls}".size, selector.to_s.size]}"
67
+ else
68
+ "glang_#{selector}"
69
+ end
70
+
71
+ # surcharge de l'helper
72
+ @module.module_eval <<-end_eval # We use module_eval to avoid leaks
73
+ alias_method :gl#{selector}, :#{selector}
74
+
75
+ def #{selector}(*args)
76
+ selector_g = '#{rlang}'.gsub('glang', I18n.locale.to_s).to_sym
77
+
78
+ #logger.debug "Call routes : #{selector} => \#{selector_g} (#{rlang}) "
79
+ #puts "Call routes : #{selector} => \#{selector_g} (#{rlang}) Found:\#{respond_to? selector_g and selector_g != :#{selector}}"
80
+ if respond_to? selector_g and selector_g != :#{selector}
81
+ send(selector_g, *args)
82
+ else
83
+ gl#{selector}(*args)
84
+ end
85
+ end
86
+ protected :gl#{selector}
87
+ end_eval
88
+
89
+ end
90
+ end
91
+ end
92
+
93
+ alias_method :gl_add_named_route, :add_named_route
94
+ def add_named_route(name, path, options = {}) #:nodoc:
95
+ if @locales and !path.blank? and !Thread.current[:i18n_no_named_localization]
96
+ # Here, try to translate standard named routes
97
+ name = name.to_s
98
+
99
+ @locales.each do |l|
100
+ I18n.locale = l
101
+ nt = "#{l}_#{name}"
102
+ if nt != name and (t = I18nRouting.translation_for(path, :named_routes_path))
103
+ gl_add_named_route(nt, t, options.merge(:glang => l))
104
+ puts("[I18n] > localize %-10s: %40s (%s) => %s" % ['route', name, l, t]) if @i18n_verbose
105
+ end
106
+ end
107
+
108
+ old_v = Thread.current[:globalized]
109
+ Thread.current[:globalized] = true
110
+ gl_add_named_route(name, path, options)
111
+ Thread.current[:globalized] = old_v
112
+ return
113
+ end
114
+
115
+ gl_add_named_route(name, path, options)
116
+ end
117
+
118
+ end
119
+ end
120
+ end
121
+
122
+ module ActionController
123
+ module Resources
124
+ class Resource
125
+ alias_method :mkd_initialize, :initialize
126
+ def initialize(entities, options)
127
+ @real_path = options.delete(:real_path)
128
+ @real_path = @real_path.to_s.singularize if @real_path
129
+
130
+ mkd_initialize(entities, options)
131
+ end
132
+
133
+ def nesting_name_prefix
134
+ @real_path ? "#{shallow_name_prefix}#{@real_path}_" : "#{shallow_name_prefix}#{singular}_"
135
+ end
136
+
137
+ def nesting_path_prefix
138
+ @nesting_path_prefix ||= (@real_path ? "#{shallow_path_prefix}/#{path_segment}/:#{@real_path}_id" : "#{shallow_path_prefix}/#{path_segment}/:#{singular}_id")
139
+ end
140
+ end
141
+
142
+ def switch_globalized_state(state)
143
+ old_g = Thread.current[:globalized]
144
+ Thread.current[:globalized] = state
145
+ yield
146
+ Thread.current[:globalized] = old_g
147
+ end
148
+
149
+ def switch_no_named_localization(state)
150
+ old_g = Thread.current[:i18n_no_named_localization]
151
+ Thread.current[:i18n_no_named_localization] = state
152
+ yield
153
+ Thread.current[:i18n_no_named_localization] = old_g
154
+ end
155
+
156
+ def create_globalized_resources(type, namespace, *entities, &block)
157
+
158
+ Thread.current[:i18n_nested_deep] ||= 0
159
+ Thread.current[:i18n_nested_deep] += 1
160
+
161
+ if @set.locales
162
+ name = entities.dup.shift.to_s
163
+
164
+ options = entities.extract_options!
165
+ opts = options.dup
166
+
167
+ locales = @set.locales
168
+ localized(nil) do
169
+ locales.each do |l|
170
+ I18n.locale = l
171
+ nt = "#{l}_#{name}"
172
+ if nt != name and (t = I18nRouting.translation_for(name, namespace))
173
+ nt = "#{l}_#{name}"
174
+ opts[:as] = t
175
+ opts[:glang] = l
176
+ opts[:controller] ||= name.to_s.pluralize
177
+ opts[:real_path] = opts[:singular] || name
178
+ opts[:path_names] = I18nRouting.path_names(name, options)
179
+ path_prefix_t = I18n.t(:path_prefix, :scope => :"routes.#{name}", :default => "NoPathPrefixTranslation")
180
+ opts[:path_prefix] = path_prefix_t unless path_prefix_t == "NoPathPrefixTranslation"
181
+
182
+ localized([l]) do
183
+ switch_no_named_localization(true) do
184
+ send(type, nt.to_sym, opts, &block)
185
+ end
186
+ end
187
+ puts("[I18n] > localize %-10s: %40s (%s) => %s" % [namespace, nt, l, t]) if @set.i18n_verbose
188
+ end
189
+ end
190
+
191
+ if Thread.current[:i18n_nested_deep] < 2
192
+ switch_no_named_localization(nil) do
193
+ switch_globalized_state(true) do
194
+ send(type, *(entities << options), &block)
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ else
201
+ send(type, *entities, &block)
202
+ end
203
+
204
+ Thread.current[:i18n_nested_deep] -= 1
205
+ end
206
+
207
+ alias_method :gl_resources, :resources
208
+ def resources(*entities, &block)
209
+ create_globalized_resources(:gl_resources, :resources, *entities, &block)
210
+ end
211
+
212
+ alias_method :gl_resource, :resource
213
+ def resource(*entities, &block)
214
+ create_globalized_resources(:gl_resource, :resource, *entities, &block)
215
+ end
216
+
217
+ private
218
+ alias_method :gl_action_options_for, :action_options_for
219
+ def action_options_for(action, resource, method = nil, resource_options = {})
220
+ opts = gl_action_options_for(action, resource, method, resource_options)
221
+
222
+ if Thread.current[:globalized]
223
+ Thread.current[:globalized] = resource.plural
224
+ if resource.uncountable?
225
+ Thread.current[:globalized] = resource.plural.to_s + '_index'
226
+ end
227
+ Thread.current[:globalized_s] = resource.singular
228
+ else
229
+ Thread.current[:globalized] = nil
230
+ Thread.current[:globalized_s] = nil
231
+ end
232
+ if resource.options[:glang]
233
+ opts[:glang] = resource.options[:glang]
234
+ end
235
+
236
+ opts
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,376 @@
1
+ # encoding: utf-8
2
+ require 'rack/mount'
3
+ require 'action_dispatch'
4
+ require 'active_support/core_ext/module'
5
+
6
+ module I18nRouting
7
+ module Mapper
8
+
9
+ private
10
+
11
+ # Just create a Mapper:Resource with given parameters
12
+ def resource_from_params(type, *resources)
13
+ res = resources.clone
14
+
15
+ options = res.extract_options!
16
+ r = res.first
17
+
18
+ type == :resource ? ActionDispatch::Routing::Mapper::SingletonResource.new(r, options.dup) : ActionDispatch::Routing::Mapper::Resource.new(r, options.dup)
19
+ end
20
+
21
+ # Localize a resources or a resource
22
+ def localized_resources(type = :resources, *resources, &block)
23
+ localizable_route = nil
24
+
25
+ if @locales
26
+ res = resources.clone
27
+
28
+ options = res.extract_options!
29
+ r = res.first
30
+
31
+ resource = type == :resource ? ActionDispatch::Routing::Mapper::SingletonResource.new(r, options.dup) : ActionDispatch::Routing::Mapper::Resource.new(r, options.dup)
32
+
33
+ # Check for translated resource
34
+ @locales.each do |locale|
35
+ I18n.locale = locale
36
+ localized_path = I18nRouting.translation_for(resource.name, type)
37
+
38
+ # A translated route exists :
39
+ if localized_path and String === localized_path
40
+ puts("[I18n] > localize %-10s: %40s (%s) => /%s" % [type, resource.name, locale, localized_path]) if @i18n_verbose
41
+ opts = options.dup
42
+ opts[:path] = localized_path
43
+ opts[:controller] ||= r.to_s.pluralize
44
+
45
+ res = ["#{locale}_#{r}".to_sym, opts]
46
+
47
+ constraints = opts[:constraints] ? opts[:constraints].dup : {}
48
+ constraints[:i18n_locale] = locale.to_s
49
+
50
+ scope(:constraints => constraints, :path_names => I18nRouting.path_names(resource.name, @scope)) do
51
+ localized_branch(locale) do
52
+ send(type, *res) do
53
+ # In the resource(s) block, we need to keep and restore some context :
54
+
55
+ if block
56
+ old_name = @scope[:i18n_real_resource_name]
57
+ old = @scope[:scope_level_resource]
58
+
59
+ @scope[:i18n_real_resource_name] = resource.name
60
+ @scope[:i18n_scope_level_resource] = old
61
+ @scope[:scope_level_resource] = resource
62
+
63
+ if type == :resource and @scope[:name_prefix]
64
+ # Need to fake name_prefix for singleton resource
65
+ @scope[:name_prefix] = @scope[:name_prefix].gsub(Regexp.new("#{old.name}$"), resource.name)
66
+ end
67
+
68
+ block.call if block
69
+
70
+ @scope[:scope_level_resource] = old
71
+ @scope[:i18n_real_resource_name] = old_name
72
+ end
73
+
74
+ @scope[:i18n_scope_level_resource] = nil
75
+
76
+ end
77
+ end
78
+ end
79
+
80
+ localizable_route = resource
81
+ end
82
+ end
83
+ end
84
+ return localizable_route
85
+ end
86
+
87
+ # Set if the next route created will be a localized route or not
88
+ # If yes, localizable is a name, or a Mapper::Resource
89
+ # Can take a block, if so, save the current context, set the new
90
+ # Call the block, then restore the old context and return the block return
91
+ def set_localizable_route(localizable)
92
+ if block_given?
93
+ old = @set.named_routes.localizable
94
+ @set.named_routes.set_localizable_route(localizable)
95
+ r = yield
96
+ @set.named_routes.set_localizable_route(old)
97
+ return r
98
+ else
99
+ @set.named_routes.set_localizable_route(localizable)
100
+ end
101
+ end
102
+
103
+ def localizable_route
104
+ @set.named_routes.localizable
105
+ end
106
+
107
+ # Return the aproximate deep in scope level
108
+ def nested_deep
109
+ (@scope and Array === @scope[:blocks] and @scope[:scope_level]) ? @scope[:blocks].size : 0
110
+ end
111
+
112
+ public
113
+
114
+ # On Routing::Mapper initialization (when doing Application.routes.draw do ...)
115
+ # prepare routing system to be i18n ready
116
+ def initialize(*args)
117
+ super
118
+
119
+ # Add i18n as valid conditions for Rack::Mount
120
+ @valid_conditions = @set.instance_eval { @set }.instance_eval { @valid_conditions }
121
+ @valid_conditions << :i18n_locale if !@valid_conditions.include?(:i18n_locale)
122
+
123
+ # Extends the current RouteSet in order to define localized helper for named routes
124
+ # When calling define_url_helper, it calls define_localized_url_helper too.
125
+ if !@set.named_routes.respond_to?(:define_localized_url_helper)
126
+ @set.named_routes.class_eval <<-END_EVAL, __FILE__, __LINE__ + 1
127
+ alias_method :localized_define_url_helper, :define_url_helper
128
+ def define_url_helper(route, name, kind, options)
129
+ localized_define_url_helper(route, name, kind, options)
130
+ define_localized_url_helper(route, name, kind, options)
131
+ end
132
+ END_EVAL
133
+
134
+ @set.named_routes.extend I18nRouting::NamedRouteCollection
135
+ end
136
+ end
137
+
138
+ # Rails 3 routing system
139
+ # Create a block for localized routes, in your routes.rb :
140
+ #
141
+ # localized do
142
+ # resources :users
143
+ # match 'about' => 'contents#about', :as => :about
144
+ # end
145
+ #
146
+ def localized(locales = I18n.available_locales, opts = {})
147
+ # Add if not added Rails.root/config/locales/*.yml in the I18n.load_path
148
+ if !@i18n_routing_path_set and defined?(Rails) and Rails.respond_to?(:root) and Rails.root
149
+ I18n.load_path = (I18n.load_path << Dir[Rails.root.join('config', 'locales', '*.yml').to_s]).uniq
150
+ @i18n_routing_path_set = true
151
+ end
152
+
153
+
154
+ old_value = @locales
155
+ @locales = locales
156
+ @i18n_verbose ||= opts.delete(:verbose)
157
+ yield
158
+ ensure
159
+ @locales = old_value
160
+ end
161
+
162
+ # Create a branch for create routes in the specified locale
163
+ def localized_branch(locale)
164
+ set_localizable_route(nil) do
165
+ old = @localized_branch
166
+ @localized_branch = locale
167
+ localized([locale]) do
168
+ yield
169
+ end
170
+ @localized_branch = old
171
+ end
172
+ end
173
+
174
+ # Set we do not want to localize next resource
175
+ def skip_localization
176
+ old = @skip_localization
177
+ @skip_localization = @localized_branch ? nil : true
178
+ yield
179
+ @skip_localization = old
180
+ end
181
+
182
+ def match(*args)
183
+ # Localize simple match only if there is no resource scope.
184
+ if args.size == 1 and @locales and !parent_resource and args.first[:as]
185
+ @locales.each do |locale|
186
+ mapping = LocalizedMapping.new(locale, @set, @scope, Marshal.load(Marshal.dump(args))) # Dump is dirty but how to make deep cloning easily ? :/
187
+ if mapping.localizable?
188
+ puts("[I18n] > localize %-10s: %40s (%s) => %s" % ['route', args.first[:as], locale, mapping.path]) if @i18n_verbose
189
+ @set.add_route(*mapping.to_route)
190
+ end
191
+ end
192
+
193
+ # Now, create the real match :
194
+ return set_localizable_route(args.first[:as]) do
195
+ super
196
+ end
197
+ end
198
+
199
+ super
200
+ end
201
+
202
+ def create_globalized_resources(type, *resources, &block)
203
+
204
+ #puts "#{' ' * nested_deep}Call #{type} : #{resources.inspect} (#{@locales.inspect}) (#{@localized_branch}) (#{@skip_localization})"
205
+
206
+ cur_scope = nil
207
+ if @locales
208
+ localized = localized_resources(type, *resources, &block) if !@skip_localization
209
+
210
+ ## We do not translate if we are in a translations branch :
211
+ return if localized and nested_deep > 0
212
+
213
+ # Set the current standard resource in order to customize url helper :
214
+ if !@localized_branch
215
+ r = resource_from_params(type, *resources)
216
+ cur_scope = (parent_resource and parent_resource.name == r.name) ? parent_resource : r
217
+ end
218
+ end
219
+
220
+ set_localizable_route(cur_scope) do
221
+ skip_localization do
222
+ #puts "#{' ' * nested_deep} \\- Call original #{type} : for #{resources.inspect}}"
223
+ send("#{type}_without_i18n_routing".to_sym, *resources, &block)
224
+ end
225
+ end
226
+
227
+ end
228
+
229
+ # Alias methods in order to handle i18n routes
230
+ def self.included(mod)
231
+ mod.send :alias_method_chain, :resource, :i18n_routing
232
+ mod.send :alias_method_chain, :resources, :i18n_routing
233
+
234
+ # Here we redefine some methods, in order to handle
235
+ # correct path_names translation on the fly
236
+ [:map_method, :member, :collection].each do |m|
237
+ rfname = "#{m}_without_i18n_routing".to_sym
238
+ mod.send :define_method, "#{m}_with_i18n_routing".to_sym do |*args, &block|
239
+
240
+ if @localized_branch and @scope[:i18n_scope_level_resource] and @scope[:i18n_real_resource_name]
241
+ o = @scope[:scope_level_resource]
242
+ @scope[:scope_level_resource] = @scope[:i18n_scope_level_resource]
243
+
244
+ pname = @scope[:path_names] || {}
245
+ pname[args[1]] = args[1]
246
+ scope(:path_names => I18nRouting.path_names(@scope[:i18n_real_resource_name], {:path_names => pname})) do
247
+ send(rfname, *args, &block)
248
+ end
249
+ @scope[:scope_level_resource] = o
250
+ return
251
+ end
252
+
253
+ send(rfname, *args, &block)
254
+
255
+ end
256
+
257
+ mod.send :alias_method_chain, m, :i18n_routing
258
+ end
259
+ end
260
+
261
+ def resource_with_i18n_routing(*resources, &block)
262
+ create_globalized_resources(:resource, *resources, &block)
263
+ end
264
+
265
+ def resources_with_i18n_routing(*resources, &block)
266
+ create_globalized_resources(:resources, *resources, &block)
267
+ end
268
+
269
+ end
270
+
271
+ # Used for localize simple named routes
272
+ class LocalizedMapping < ActionDispatch::Routing::Mapper::Mapping
273
+
274
+ attr_reader :path
275
+
276
+ def initialize(locale, set, scope, args)
277
+ super(set, scope, args.clone)
278
+
279
+ # try to get translated path :
280
+ I18n.locale = locale
281
+ ts = @path.gsub(/^\//, '')
282
+ @localized_path = '/' + (I18nRouting.translation_for(ts, :named_routes_path) || ts)
283
+
284
+ # If a translated path exists, set localized infos
285
+ if @localized_path and @localized_path != @path
286
+ #@options[:controller] ||= @options[:as]
287
+ @options[:as] = "#{locale}_#{@options[:as]}"
288
+ @path = @localized_path
289
+ @options[:constraints] = @options[:constraints] ? @options[:constraints].dup : {}
290
+ @options[:constraints][:i18n_locale] = locale.to_s
291
+ @options[:anchor] = true
292
+ else
293
+ @localized_path = nil
294
+ end
295
+
296
+ end
297
+
298
+ # Return true if this route is localizable
299
+ def localizable?
300
+ @localized_path != nil
301
+ end
302
+
303
+ end
304
+
305
+ module NamedRouteCollection
306
+
307
+ attr_reader :localizable
308
+
309
+ def set_localizable_route(localizable)
310
+ @localizable = localizable
311
+ end
312
+
313
+ # Alias named route helper in order to check if a localized helper exists
314
+ # If not use the standard one.
315
+ def define_localized_url_helper(route, name, kind, options)
316
+ if n = localizable
317
+ selector = url_helper_name(name, kind)
318
+
319
+ rlang = if n.kind_of?(ActionDispatch::Routing::Mapper::Resources::Resource) and i = name.to_s.rindex("_#{n.plural}")
320
+ "#{selector.to_s[0, i]}_glang_#{n.plural}#{selector.to_s[i + "_#{n.plural}".size, selector.to_s.size]}"
321
+ elsif n.kind_of?(ActionDispatch::Routing::Mapper::Resources::Resource) and i = name.to_s.rindex("_#{n.singular}")
322
+ "#{selector.to_s[0, i]}_glang_#{n.singular}#{selector.to_s[i + "_#{n.singular}".size, selector.to_s.size]}"
323
+ else
324
+ "glang_#{selector}"
325
+ end
326
+
327
+ @module.module_eval <<-end_eval # We use module_eval to avoid leaks
328
+ alias_method :localized_#{selector}, :#{selector}
329
+
330
+ def #{selector}(*args)
331
+ selector_g = '#{rlang}'.gsub('glang', I18n.locale.to_s).to_sym
332
+
333
+ #puts "Call routes : #{selector} => \#{selector_g} (\#{I18n.locale}) "
334
+ if respond_to? selector_g and selector_g != :#{selector}
335
+ send(selector_g, *args)
336
+ else
337
+ localized_#{selector}(*args)
338
+ end
339
+ end
340
+
341
+ end_eval
342
+
343
+ end
344
+ end
345
+ end
346
+
347
+ # Rack::Mount::Route module
348
+ # Exists in order to use apropriate localized route when using url_for
349
+ module RackMountRoute
350
+
351
+ # Alias methods in order to handle i18n routes
352
+ def self.included(mod)
353
+ mod.send :alias_method_chain, :generate, :i18n_routing
354
+ mod.send :alias_method_chain, :initialize, :i18n_routing
355
+ end
356
+
357
+ # During route initialization, if a condition i18n_locale is present
358
+ # Delete it, and store it in @locale
359
+ def initialize_with_i18n_routing(app, conditions, defaults, name)
360
+ @locale = conditions[:i18n_locale] ? conditions.delete(:i18n_locale).source.to_sym : nil
361
+ initialize_without_i18n_routing(app, conditions, defaults, name)
362
+ end
363
+
364
+ # Called for dynamic route generation
365
+ # If a @locale is present and if this locale is not the current one
366
+ # => return nil and refuse to generate the route
367
+ def generate_with_i18n_routing(method, params = {}, recall = {}, options = {})
368
+ return nil if @locale and @locale != I18n.locale.to_sym
369
+ generate_without_i18n_routing(method, params, recall, options)
370
+ end
371
+
372
+ end
373
+ end
374
+
375
+ ActionDispatch::Routing::Mapper.send :include, I18nRouting::Mapper
376
+ Rack::Mount::Route.send :include, I18nRouting::RackMountRoute
@@ -0,0 +1,309 @@
1
+ require 'spec_helper'
2
+
3
+ describe :localized_routes do
4
+
5
+ $r = nil # Global routes in order to speed up testing
6
+
7
+ before(:all) do
8
+
9
+ if !$r
10
+ if !rails3?
11
+ ActionController::Routing::Routes.clear!
12
+ ActionController::Routing::Routes.draw do |map|
13
+ map.not_about 'not_about', :controller => 'not_about'
14
+ map.resources :not_users
15
+ map.resource :not_contact
16
+
17
+ map.localized(I18n.available_locales, :verbose => false) do
18
+ map.about 'about', :controller => 'about', :action => :show
19
+
20
+ map.resources :users, :member => {:level => :get}, :collection => {:groups => :get}
21
+ map.resource :contact
22
+
23
+ map.resources :authors do |m|
24
+ m.resources :books
25
+ end
26
+
27
+ map.resource :foo do |m|
28
+ m.resources :bars
29
+ m.resource :foofoo do |mm|
30
+ mm.resources :bars
31
+ end
32
+ end
33
+
34
+ map.resources :universes do |m|
35
+ m.resources :galaxies do |mm|
36
+ mm.resources :planets do |mmm|
37
+ mmm.resources :countries
38
+ end
39
+ end
40
+ end
41
+
42
+ map.resources :drones, :path_prefix => 'doubledrones/unimatrix/zero'
43
+ end
44
+ end
45
+
46
+ $r = ActionController::Routing::Routes
47
+
48
+ class UrlTester
49
+ include ActionController::UrlWriter
50
+ end
51
+
52
+ else
53
+
54
+ $r = ActionDispatch::Routing::RouteSet.new
55
+ $r.draw do
56
+ match 'not_about' => "not_about#show", :as => :not_about
57
+ resources :not_users
58
+ resource :not_contact
59
+
60
+ localized(I18n.available_locales, :verbose => false) do
61
+ match 'about' => "about#show", :as => :about
62
+
63
+ resources :users do
64
+ member do
65
+ get :level
66
+ end
67
+ get :groups, :on => :collection
68
+ end
69
+ resource :contact
70
+
71
+ resources :authors do
72
+ resources :books
73
+ end
74
+
75
+ resource :foo do
76
+ resources :bars
77
+ resource :foofoo do
78
+ resources :bars
79
+ end
80
+ end
81
+
82
+ resources :universes do
83
+ resources :galaxies do
84
+ resources :planets do
85
+ scope do
86
+ resources :countries
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ end
93
+ end
94
+
95
+ class UrlTester; end
96
+ UrlTester.send :include, $r.url_helpers
97
+
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ let(:nested_routes) { $r.named_routes.instance_eval { routes } }
104
+ let(:routes) { UrlTester.new }
105
+
106
+ def url_for(opts)
107
+ $r.generate_extras(opts).first
108
+ end
109
+
110
+ context "do not break existing behavior" do
111
+
112
+ it "of named_routes" do
113
+ routes.send(:not_about_path).should == "/not_about"
114
+ end
115
+
116
+ it "of a singular resource" do
117
+ routes.send(:not_contact_path).should == "/not_contact"
118
+ end
119
+
120
+ it "of resources" do
121
+ routes.send(:not_users_path).should == "/not_users"
122
+ end
123
+
124
+ end
125
+
126
+ context "for default routes" do
127
+
128
+ before do
129
+ I18n.locale = :de # Not localized
130
+ end
131
+
132
+ it "named_route uses default values" do
133
+ routes.send(:about_path).should == "/about"
134
+ end
135
+
136
+ it "resource generates routes using default values" do
137
+ routes.send(:contact_path).should == "/contact"
138
+ end
139
+
140
+ it "resources generates routes using default values" do
141
+ routes.send(:users_path).should == "/users"
142
+ end
143
+
144
+ it "url_for generates route using default values" do
145
+ url_for(:controller => :users).should == "/users"
146
+ end
147
+
148
+ it "nested resources generate routes using default values" do
149
+ routes.send(:author_books_path, 1).should == "/authors/1/books"
150
+ end
151
+
152
+ it "deep nested resources generate routes using default values" do
153
+ routes.send(:universes_path).should == "/universes"
154
+ routes.send(:universe_galaxies_path, 1).should == "/universes/1/galaxies"
155
+ routes.send(:universe_galaxy_planets_path, 1, 1).should == "/universes/1/galaxies/1/planets"
156
+ end
157
+
158
+ it "single resource should have by default the pluralized controller" do
159
+ nested_routes[:foo].defaults[:controller].should == 'foos'
160
+ end
161
+
162
+ end
163
+
164
+ context "" do
165
+
166
+ before do
167
+ I18n.locale = :fr
168
+ end
169
+
170
+ it "named_route generates route using localized values" do
171
+ routes.send(:about_path).should == "/#{I18n.t :about, :scope => :named_routes_path}"
172
+ end
173
+
174
+ it "named_route generates route using localized values and I18n.locale as a string" do
175
+ o = I18n.locale
176
+ I18n.locale = "fr"
177
+ routes.send(:about_path).should == "/#{I18n.t :about, :scope => :named_routes_path}"
178
+ I18n.locale = o
179
+ end
180
+
181
+ it "resource generates routes using localized values" do
182
+ routes.send(:contact_path).should == "/#{I18n.t :contact, :scope => :resource}"
183
+ end
184
+
185
+ it "resources generates routes using localized values" do
186
+ routes.send(:users_path).should == "/#{I18n.t :as, :scope => :'routes.users'}"
187
+ end
188
+
189
+ it "url_for generates routes using localized values" do
190
+ url_for(:controller => :users).should == "/#{I18n.t :as, :scope => :'routes.users'}"
191
+ url_for(:controller => :about, :action => :show).should == "/#{I18n.t :about, :scope => :named_routes_path}"
192
+ end
193
+
194
+ it "url_for generates routes for drones with path prefix" do
195
+ url_for(:controller => :drones).should == "#{I18n.t :path_prefix, :scope => :'routes.drones'}/#{I18n.t :as, :scope => :'routes.drones'}"
196
+ end
197
+
198
+ it "nested resources generate routes using localized values" do
199
+ routes.send(:author_books_path, 1).should == "/#{I18n.t :authors, :scope => :resources}/1/#{I18n.t :books, :scope => :resources}"
200
+ end
201
+
202
+ it "deep nested resources generate routes using localized values and translate routes even translated name is the same" do
203
+ routes.send(:universes_path).should == "/#{I18n.t :universes, :scope => :resources}"
204
+ routes.send(:universe_galaxies_path, 1).should == "/#{I18n.t :universes, :scope => :resources}/1/#{I18n.t :galaxies, :scope => :resources}"
205
+ routes.send(:universe_galaxy_planets_path, 1, 1).should == "/#{I18n.t :universes, :scope => :resources}/1/#{I18n.t :galaxies, :scope => :resources}/1/#{I18n.t :planets, :scope => :resources}"
206
+ routes.send(:universe_galaxy_planet_countries_path, 1, 1, 42).should == "/#{I18n.t :universes, :scope => :resources}/1/#{I18n.t :galaxies, :scope => :resources}/1/#{I18n.t :planets, :scope => :resources}/42/#{I18n.t :countries, :scope => :resources}"
207
+ end
208
+
209
+ context "with path_names" do
210
+
211
+ it "default translated path names" do
212
+ routes.send(:new_universe_path).should == "/#{I18n.t :universes, :scope => :resources}/#{I18n.t :new, :scope => :path_names}"
213
+ routes.send(:edit_universe_path, 42).should == "/#{I18n.t :universes, :scope => :resources}/42/#{I18n.t :edit, :scope => :path_names}"
214
+ end
215
+
216
+ it "custom translated path names" do
217
+ routes.send(:new_user_path).should == "/#{I18n.t :users, :scope => :resources}/#{I18n.t :new, :scope => :'routes.users.path_names'}"
218
+ routes.send(:edit_user_path, 42).should == "/#{I18n.t :users, :scope => :resources}/42/#{I18n.t :edit, :scope => :'routes.users.path_names'}"
219
+ end
220
+
221
+ end
222
+
223
+ context "with member and collection" do
224
+
225
+ it "custom member" do
226
+ I18n.locale = :en
227
+ routes.send(:level_user_path, 42).should == "/#{I18n.t :users, :scope => :resources}/42/level"
228
+ I18n.locale = :fr
229
+ routes.send(:level_user_path, 42).should == "/#{I18n.t :users, :scope => :resources}/42/#{I18n.t :level, :scope => :'routes.users.path_names'}"
230
+ end
231
+
232
+ it "custom collection" do
233
+ I18n.locale = :en
234
+ routes.send(:groups_users_path).should == "/#{I18n.t :users, :scope => :resources}/groups"
235
+ I18n.locale = :fr
236
+ routes.send(:groups_users_path).should == "/#{I18n.t :users, :scope => :resources}/#{I18n.t :groups, :scope => :'routes.users.path_names'}"
237
+ end
238
+
239
+ end
240
+
241
+ context "when nested" do
242
+
243
+ it "named routes should not be nil" do
244
+ nested_routes[:author_fr_books].should_not be_nil
245
+ end
246
+
247
+ context "in Rails #{Rails.version}" do
248
+ it "include the correct significant keys" do
249
+ v = !rails3? ? :significant_keys : :segment_keys
250
+ nested_routes[:author_books].send(v).should include(:author_id)
251
+ nested_routes[:author_fr_books].send(v).should include(:author_id)
252
+ end
253
+ end
254
+
255
+ end
256
+
257
+ context "when nested inside a singleton resource" do
258
+
259
+ it "named routes should have locale placed at correct position" do
260
+ nested_routes[:fr_foo_fr_bars].should be_nil
261
+ nested_routes[:foo_fr_bars].should_not be_nil
262
+ end
263
+
264
+ it "routes for the singleton resource alone should be translated correctly" do
265
+ routes.send(:foo_path).should == "/#{I18n.t :foo, :scope => :resource}"
266
+ end
267
+
268
+ it "routes should be translated correctly" do
269
+ routes.send(:foo_bars_path).should == "/#{I18n.t :foo, :scope => :resource}/#{I18n.t :bars, :scope => :resources}"
270
+ end
271
+
272
+ it "routes should be translated correctly also with deep nested singleton resource" do
273
+ routes.send(:foo_foofoo_bars_path).should == "/#{I18n.t :foo, :scope => :resource}/#{I18n.t :foofoo, :scope => :resource}/#{I18n.t :bars, :scope => :resources}"
274
+ end
275
+
276
+ end
277
+
278
+ context "when deeply nested" do
279
+
280
+ it "named routes should not be nil" do
281
+ nested_routes[:universe_galaxy_fr_planet].should_not be_nil
282
+ end
283
+
284
+ context "in Rails #{Rails.version}" do
285
+ it "include the correct significant keys" do
286
+ v = !rails3? ? :significant_keys : :segment_keys
287
+ nested_routes[:universe_galaxy_planet].send(v).should include(:universe_id)
288
+ nested_routes[:universe_galaxy_fr_planet].send(v).should include(:universe_id)
289
+ nested_routes[:universe_galaxy_planet].send(v).should include(:galaxy_id)
290
+ nested_routes[:universe_galaxy_fr_planet].send(v).should include(:galaxy_id)
291
+ end
292
+ end
293
+ end
294
+
295
+ it "nested resources do not deep translate with multi helpers" do
296
+ nested_routes.keys.should_not include(:fr_author_books) # Do not want fr_author_books
297
+ end
298
+
299
+ end
300
+
301
+ # context "just output" do
302
+ # it "output all routes properly" do
303
+ # nested_routes.keys.collect(&:to_s).sort.each do |k|
304
+ # puts("%50s: %.80s" % [k, (nested_routes[k.to_sym].path rescue nested_routes[k.to_sym].to_s)])
305
+ # end
306
+ # end
307
+ # end
308
+
309
+ end
@@ -0,0 +1,13 @@
1
+ en:
2
+ resources:
3
+ users: members
4
+ authors: 'authors-en'
5
+ galaxies: 'galaxies-en'
6
+ planets: 'planets-en'
7
+ universes: 'universes-en'
8
+ bars: bars_en
9
+ resource:
10
+ foo: foo_en
11
+ contact: 'contact-us'
12
+ named_routes_path:
13
+ about: 'about-our-company'
@@ -0,0 +1,30 @@
1
+ fr:
2
+ routes:
3
+ users:
4
+ as: utilisateurs
5
+ path_names:
6
+ level: 'niveau'
7
+ groups: 'groupe'
8
+ new: 'nouvel_utilisateur'
9
+ edit: 'edition_utilisateur'
10
+ drones:
11
+ as: senord
12
+ path_prefix: '/senordelbuod/xirtaminu/orez'
13
+ resources:
14
+ users: utilisateurs
15
+ authors: 'auteurs'
16
+ books: 'livres'
17
+ galaxies: 'galaxies'
18
+ planets: 'planetes'
19
+ universes: 'univers'
20
+ countries: 'pays'
21
+ bars: bars_fr
22
+ resource:
23
+ foo: foo_fr
24
+ foofoo: froufrou
25
+ contact: 'contactez-nous'
26
+ named_routes_path:
27
+ about: 'a-propos'
28
+ path_names:
29
+ new: 'nouveau'
30
+ edit: 'edition'
@@ -0,0 +1,42 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ $rails_version = ARGV.find { |e| e =~ /rails_spec_version=.*/ }.split('=').last.to_i rescue nil
5
+
6
+ if !$rails_version
7
+ begin
8
+ require 'rails'
9
+ rescue Exception
10
+ $rails_version = 2
11
+ end
12
+ end
13
+
14
+ if !Module.constants.include?('Rails') and $rails_version
15
+ module Rails
16
+ def self.version
17
+ $rails_version.to_s
18
+ end
19
+ end
20
+ end
21
+
22
+ if Rails.version < '3'
23
+ gem 'actionpack', '< 2.9.9'
24
+ require 'action_controller'
25
+ require 'action_controller/routing'
26
+ else
27
+ gem 'actionpack', '> 2.9'
28
+ require 'action_controller'
29
+ require 'action_dispatch'
30
+ require 'rack/mount'
31
+ end
32
+
33
+ def rails3?
34
+ !(Rails.version < '3')
35
+ end
36
+
37
+ puts "Launching spec for Rails #{Rails.version}"
38
+
39
+ # Add I18n load_path
40
+ I18n.load_path = (I18n.load_path << Dir[File.join(File.dirname(__FILE__), 'locales', '*.yml')]).uniq
41
+
42
+ require File.dirname(__FILE__) + '/../init.rb'
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: doubledrones-i18n_routing
3
+ version: !ruby/object:Gem::Version
4
+ hash: 77
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 3
9
+ - 6
10
+ - 1
11
+ version: 0.3.6.1
12
+ platform: ruby
13
+ authors:
14
+ - Guillaume Luccisano
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-08-11 00:00:00 +02:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: i18n
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">"
29
+ - !ruby/object:Gem::Version
30
+ hash: 25
31
+ segments:
32
+ - 0
33
+ - 3
34
+ - 5
35
+ version: 0.3.5
36
+ type: :runtime
37
+ version_requirements: *id001
38
+ description: I18n_routing is a plugin for Ruby on Rails that lets you easily translate your routes trough the I18n api included in Rails since version 2.2
39
+ email: guillaume.luccisano@gmail.com
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ extra_rdoc_files: []
45
+
46
+ files:
47
+ - lib/i18n_routing.rb
48
+ - lib/i18n_routing_common.rb
49
+ - lib/i18n_routing_rails2.rb
50
+ - lib/i18n_routing_rails3.rb
51
+ - spec/i18n_routing/i18n_spec.rb
52
+ - spec/locales/en.yml
53
+ - spec/locales/fr.yml
54
+ - spec/spec_helper.rb
55
+ - CHANGELOG.rdoc
56
+ - MIT-LICENSE
57
+ - Rakefile
58
+ - README.rdoc
59
+ - init.rb
60
+ has_rdoc: true
61
+ homepage: http://github.com/kwi/i18n_routing
62
+ licenses: []
63
+
64
+ post_install_message:
65
+ rdoc_options: []
66
+
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ hash: 3
75
+ segments:
76
+ - 0
77
+ version: "0"
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ hash: 19
84
+ segments:
85
+ - 1
86
+ - 3
87
+ - 4
88
+ version: 1.3.4
89
+ requirements: []
90
+
91
+ rubyforge_project: doubledrones-i18n_routing
92
+ rubygems_version: 1.3.7
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: I18n routing module for Rails 2.3.x and Rails 3. Translate your routes with ease !
96
+ test_files: []
97
+