i18n_routing 0.2.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 +11 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +15 -0
- data/Rakefile +28 -0
- data/init.rb +2 -0
- data/lib/i18n_routing.rb +6 -0
- data/lib/i18n_routing_rails2.rb +238 -0
- data/lib/i18n_routing_rails3.rb +242 -0
- data/spec/i18n_routing/i18n_spec.rb +202 -0
- data/spec/locales/en.yml +11 -0
- data/spec/locales/fr.yml +13 -0
- data/spec/spec_helper.rb +37 -0
- metadata +75 -0
data/CHANGELOG.rdoc
ADDED
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,15 @@
|
|
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
|
+
Warning: The plugin is not currently running well with deep nesting resources for Rails3. This will be fixed soon
|
8
|
+
|
9
|
+
== TODO
|
10
|
+
|
11
|
+
* Do nested resources for Rails3
|
12
|
+
* Another bug fix ?
|
13
|
+
|
14
|
+
|
15
|
+
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 -- rails_spec_version=#{@rails_spec_version}"] : ["-c"]
|
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
data/lib/i18n_routing.rb
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
# encoding: utf8
|
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 == :#{@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
|
+
#puts "ADD NAMED ROUTE : #{path}"
|
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 = I18n.t(path.to_s, :scope => :named_routes_path, :default => path.to_s)) != 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
|
+
opts[:controller] ||= name
|
168
|
+
|
169
|
+
locales = @set.locales
|
170
|
+
translated = nil
|
171
|
+
localized(nil) do
|
172
|
+
locales.each do |l|
|
173
|
+
I18n.locale = l
|
174
|
+
nt = "#{l}_#{name}"
|
175
|
+
if nt != name and ((t = I18n.t(name, :scope => namespace, :default => name)) != name)
|
176
|
+
nt = "#{l}_#{name}"
|
177
|
+
opts[:as] = t
|
178
|
+
opts[:glang] = l
|
179
|
+
opts[:real_path] = opts[:singular] || name
|
180
|
+
localized([l]) do
|
181
|
+
translated = true
|
182
|
+
switch_no_named_localization(true) do
|
183
|
+
send(type, nt.to_sym, opts, &block)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
puts("[I18n] > localize %-10s: %40s (%s) => %s" % [namespace, nt, l, t]) if @set.i18n_verbose
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
if Thread.current[:i18n_nested_deep] < 2
|
191
|
+
switch_no_named_localization(nil) do
|
192
|
+
switch_globalized_state(true) do
|
193
|
+
send(type, *(entities << options), &block)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
else
|
200
|
+
send(type, *entities, &block)
|
201
|
+
end
|
202
|
+
|
203
|
+
Thread.current[:i18n_nested_deep] -= 1
|
204
|
+
end
|
205
|
+
|
206
|
+
alias_method :gl_resources, :resources
|
207
|
+
def resources(*entities, &block)
|
208
|
+
create_globalized_resources(:gl_resources, :resources, *entities, &block)
|
209
|
+
end
|
210
|
+
|
211
|
+
alias_method :gl_resource, :resource
|
212
|
+
def resource(*entities, &block)
|
213
|
+
create_globalized_resources(:gl_resource, :resource, *entities, &block)
|
214
|
+
end
|
215
|
+
|
216
|
+
private
|
217
|
+
alias_method :gl_action_options_for, :action_options_for
|
218
|
+
def action_options_for(action, resource, method = nil, resource_options = {})
|
219
|
+
opts = gl_action_options_for(action, resource, method, resource_options)
|
220
|
+
|
221
|
+
if Thread.current[:globalized]
|
222
|
+
Thread.current[:globalized] = resource.plural
|
223
|
+
if resource.uncountable?
|
224
|
+
Thread.current[:globalized] = resource.plural.to_s + '_index'
|
225
|
+
end
|
226
|
+
Thread.current[:globalized_s] = resource.singular
|
227
|
+
else
|
228
|
+
Thread.current[:globalized] = nil
|
229
|
+
Thread.current[:globalized_s] = nil
|
230
|
+
end
|
231
|
+
if resource.options[:glang]
|
232
|
+
opts[:glang] = resource.options[:glang]
|
233
|
+
end
|
234
|
+
|
235
|
+
opts
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
# encoding: utf8
|
2
|
+
require 'rack/mount'
|
3
|
+
require 'action_dispatch'
|
4
|
+
|
5
|
+
module I18nRouting
|
6
|
+
module Mapper
|
7
|
+
|
8
|
+
private
|
9
|
+
# Localize a resources or a resource
|
10
|
+
def localized_resources(type = :resources, *resources, &block)
|
11
|
+
localizable_route = nil
|
12
|
+
|
13
|
+
if @locales
|
14
|
+
res = resources.clone
|
15
|
+
|
16
|
+
options = res.extract_options!
|
17
|
+
r = res.first
|
18
|
+
resource = type == :resource ? ActionDispatch::Routing::Mapper::SingletonResource.new(r, options) : ActionDispatch::Routing::Mapper::Resource.new(r, options)
|
19
|
+
|
20
|
+
# Check for translated resource
|
21
|
+
@locales.each do |locale|
|
22
|
+
I18n.locale = locale
|
23
|
+
localized_path = I18n.t(resource.name, :scope => type, :default => resource.name.to_s)
|
24
|
+
|
25
|
+
# A translated route exists :
|
26
|
+
if localized_path and localized_path != resource.name.to_s
|
27
|
+
puts("[I18n] > localize %-10s: %40s (%s) => %s" % [type, resource.name, locale, localized_path]) if @i18n_verbose
|
28
|
+
opts = options.dup
|
29
|
+
opts[:path] = localized_path.to_sym
|
30
|
+
opts[:controller] ||= r
|
31
|
+
opts[:constraints] = opts[:constraints] ? opts[:constraints].dup : {}
|
32
|
+
opts[:constraints][:i18n_locale] = locale.to_s
|
33
|
+
res = ["#{locale}_#{r}".to_sym, opts]
|
34
|
+
|
35
|
+
# Create the localized resource(s)
|
36
|
+
scope(:constraints => opts[:constraints]) do
|
37
|
+
localized(nil) do
|
38
|
+
send(type, *res, &block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
localizable_route = resource
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
return localizable_route
|
47
|
+
end
|
48
|
+
|
49
|
+
# Set if the next route creation will be a localied route or not
|
50
|
+
# If yes, localizable is a name, or a Mapper::Resource
|
51
|
+
# Can take a block, if so, save the current context, set the new
|
52
|
+
# Call the block, then restore the old context and return the block return
|
53
|
+
def set_localizable_route(localizable)
|
54
|
+
if block_given?
|
55
|
+
old = @set.named_routes.localizable
|
56
|
+
@set.named_routes.set_localizable_route(localizable)
|
57
|
+
r = yield
|
58
|
+
@set.named_routes.set_localizable_route(old)
|
59
|
+
return r
|
60
|
+
else
|
61
|
+
@set.named_routes.set_localizable_route(localizable)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
public
|
66
|
+
|
67
|
+
# On Routing::Mapper initialization (when doing Application.routes.draw do ...)
|
68
|
+
# prepare routing system to be i18n ready
|
69
|
+
def initialize(*args)
|
70
|
+
super
|
71
|
+
|
72
|
+
# Add i18n as valid conditions for Rack::Mount
|
73
|
+
@valid_conditions = @set.instance_eval { @set }.instance_eval { @valid_conditions }
|
74
|
+
@valid_conditions << :i18n_locale if !@valid_conditions.include?(:i18n_locale)
|
75
|
+
|
76
|
+
# Extends the current RouteSet in order to define localized helper for named routes
|
77
|
+
# When calling define_url_helper, it calls define_localized_url_helper too.
|
78
|
+
if !@set.named_routes.respond_to?(:define_localized_url_helper)
|
79
|
+
@set.named_routes.class_eval <<-END_EVAL, __FILE__, __LINE__ + 1
|
80
|
+
alias_method :localized_define_url_helper, :define_url_helper
|
81
|
+
def define_url_helper(route, name, kind, options)
|
82
|
+
localized_define_url_helper(route, name, kind, options)
|
83
|
+
define_localized_url_helper(route, name, kind, options)
|
84
|
+
end
|
85
|
+
END_EVAL
|
86
|
+
|
87
|
+
@set.named_routes.extend I18nRouting::NamedRouteCollection
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Rails 3 routing system
|
92
|
+
# Create a block for localized routes, in your routes.rb :
|
93
|
+
#
|
94
|
+
# localized do
|
95
|
+
# resources :users
|
96
|
+
# match 'about' => 'contents#about', :as => :about
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
def localized(locales = I18n.available_locales, opts = {})
|
100
|
+
old_value = @locales
|
101
|
+
@locales = locales
|
102
|
+
@i18n_verbose = opts.delete(:verbose)
|
103
|
+
yield
|
104
|
+
ensure
|
105
|
+
@locales = old_value
|
106
|
+
end
|
107
|
+
|
108
|
+
def match(*args)
|
109
|
+
# Localize simple match only if there is no resource scope.
|
110
|
+
if args.size == 1 and @locales and !@scope[:scope_level_resource] and args.first[:as]
|
111
|
+
@locales.each do |locale|
|
112
|
+
mapping = LocalizedMapping.new(locale, @set, @scope, Marshal.load(Marshal.dump(args))) # Dump is dirty but how to make deep cloning easily ? :/
|
113
|
+
if mapping.localizable?
|
114
|
+
puts("[I18n] > localize %-10s: %40s (%s) => %s" % ['route', args.first[:as], locale, mapping.path]) if @i18n_verbose
|
115
|
+
@set.add_route(*mapping.to_route)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Now, create the real match :
|
120
|
+
return set_localizable_route(args.first[:as]) do
|
121
|
+
super
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
super
|
126
|
+
end
|
127
|
+
|
128
|
+
def resource(*resources, &block)
|
129
|
+
set_localizable_route(nil) do
|
130
|
+
set_localizable_route(localized_resources(:resource, *resources, &block))
|
131
|
+
super
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def resources(*resources, &block)
|
136
|
+
set_localizable_route(nil) do
|
137
|
+
set_localizable_route(localized_resources(:resources, *resources, &block))
|
138
|
+
super
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
# Used for localize simple named routes
|
145
|
+
class LocalizedMapping < ActionDispatch::Routing::Mapper::Mapping
|
146
|
+
|
147
|
+
attr_reader :path
|
148
|
+
|
149
|
+
def initialize(locale, set, scope, args)
|
150
|
+
super(set, scope, args.clone)
|
151
|
+
|
152
|
+
# try to get translated path :
|
153
|
+
I18n.locale = locale
|
154
|
+
ts = @path.gsub(/^\//, '')
|
155
|
+
@localized_path = '/' + I18n.t(ts, :scope => :named_routes_path, :default => ts)
|
156
|
+
|
157
|
+
# If a translated path exists, set localized infos
|
158
|
+
if @localized_path and @localized_path != @path
|
159
|
+
#@options[:controller] ||= @options[:as]
|
160
|
+
@options[:as] = "#{locale}_#{@options[:as]}".to_sym
|
161
|
+
@path = @localized_path
|
162
|
+
@options[:constraints] = @options[:constraints] ? @options[:constraints].dup : {}
|
163
|
+
@options[:constraints][:i18n_locale] = locale.to_s
|
164
|
+
@options[:anchor] = true
|
165
|
+
else
|
166
|
+
@localized_path = nil
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
# Return true if this route is localizable
|
172
|
+
def localizable?
|
173
|
+
@localized_path != nil
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
module NamedRouteCollection
|
179
|
+
|
180
|
+
attr_reader :localizable
|
181
|
+
|
182
|
+
def set_localizable_route(localizable)
|
183
|
+
@localizable = localizable
|
184
|
+
end
|
185
|
+
|
186
|
+
# Alias named route helper in order to check if a localized helper exists
|
187
|
+
# If not use the standard one.
|
188
|
+
def define_localized_url_helper(route, name, kind, options)
|
189
|
+
if n = localizable
|
190
|
+
selector = url_helper_name(name, kind)
|
191
|
+
|
192
|
+
rlang = if n.kind_of?(ActionDispatch::Routing::Mapper::Resources::Resource) and i = name.to_s.rindex("_#{n.plural}")
|
193
|
+
"#{selector.to_s[0, i]}_glang_#{n.plural}#{selector.to_s[i + "_#{n.plural}".size, selector.to_s.size]}"
|
194
|
+
elsif n.kind_of?(ActionDispatch::Routing::Mapper::Resources::Resource) and i = name.to_s.rindex("_#{n.singular}")
|
195
|
+
"#{selector.to_s[0, i]}_glang_#{n.singular}#{selector.to_s[i + "_#{n.singular}".size, selector.to_s.size]}"
|
196
|
+
else
|
197
|
+
"glang_#{selector}"
|
198
|
+
end
|
199
|
+
|
200
|
+
@module.module_eval <<-end_eval # We use module_eval to avoid leaks
|
201
|
+
alias_method :localized_#{selector}, :#{selector}
|
202
|
+
|
203
|
+
def #{selector}(*args)
|
204
|
+
selector_g = '#{rlang}'.gsub('glang', I18n.locale.to_s).to_sym
|
205
|
+
|
206
|
+
#puts "Call routes : #{selector} => \#{selector_g} (\#{I18n.locale}) "
|
207
|
+
if respond_to? selector_g and selector_g != :#{selector}
|
208
|
+
send(selector_g, *args)
|
209
|
+
else
|
210
|
+
localized_#{selector}(*args)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end_eval
|
214
|
+
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Rack::Mount::Route module
|
220
|
+
# Exists in order to use apropriate localized route when using url_for
|
221
|
+
module RackMountRoute
|
222
|
+
|
223
|
+
# During route initialization, if a condition i18n_locale is present
|
224
|
+
# Delete it, and store it in @locale
|
225
|
+
def initialize(app, conditions, defaults, name)
|
226
|
+
@locale = conditions[:i18n_locale] ? conditions.delete(:i18n_locale).source.to_sym : nil
|
227
|
+
super
|
228
|
+
end
|
229
|
+
|
230
|
+
# Called for dynamic route generation
|
231
|
+
# If a @locale is present and if this locale is not the current one
|
232
|
+
# => return nil and refuse to generate the route
|
233
|
+
def generate(method, params = {}, recall = {}, options = {})
|
234
|
+
return nil if @locale and @locale != I18n.locale
|
235
|
+
super
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
ActionDispatch::Routing::Mapper.send :include, I18nRouting::Mapper
|
242
|
+
Rack::Mount::Route.send :include, I18nRouting::RackMountRoute
|
@@ -0,0 +1,202 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe :localized_routes do
|
4
|
+
before(:all) do
|
5
|
+
|
6
|
+
puts "Route for localize spec"
|
7
|
+
ActionController::Routing::Routes.clear!
|
8
|
+
|
9
|
+
if Rails.version < '3'
|
10
|
+
|
11
|
+
ActionController::Routing::Routes.draw do |map|
|
12
|
+
map.not_about 'not_about', :controller => 'not_about'
|
13
|
+
map.resources :not_users
|
14
|
+
map.resource :not_contact
|
15
|
+
|
16
|
+
map.localized do
|
17
|
+
map.about 'about', :controller => 'about', :action => :show
|
18
|
+
|
19
|
+
map.resources :users
|
20
|
+
map.resource :contact
|
21
|
+
|
22
|
+
map.resources :authors do |m|
|
23
|
+
m.resources :books
|
24
|
+
end
|
25
|
+
|
26
|
+
map.resources :universes do |m|
|
27
|
+
m.resources :galaxies do |mm|
|
28
|
+
mm.resources :planets
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
else
|
35
|
+
|
36
|
+
ActionController::Routing::Routes.draw do
|
37
|
+
match 'not_about' => "not_about#show", :as => :not_about
|
38
|
+
resources :not_users
|
39
|
+
resource :not_contact
|
40
|
+
|
41
|
+
localized(I18n.available_locales, :verbose => true) do
|
42
|
+
match 'about' => "about#show", :as => :about
|
43
|
+
|
44
|
+
resources :users
|
45
|
+
resource :contact
|
46
|
+
|
47
|
+
resources :authors do
|
48
|
+
resources :books
|
49
|
+
end
|
50
|
+
|
51
|
+
resources :universes do
|
52
|
+
resources :galaxies do
|
53
|
+
resources :planets
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
class UrlTester
|
64
|
+
include ActionController::UrlWriter
|
65
|
+
end
|
66
|
+
|
67
|
+
@routes = UrlTester.new
|
68
|
+
end
|
69
|
+
|
70
|
+
def routes
|
71
|
+
@routes
|
72
|
+
end
|
73
|
+
|
74
|
+
def url_for(opts)
|
75
|
+
ActionController::Routing::Routes.generate_extras(opts).first
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should still work for non localized named_route" do
|
79
|
+
routes.send(:not_about_path).should == "/not_about"
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should still work for non localized resource" do
|
83
|
+
routes.send(:not_contact_path).should == "/not_contact"
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should still work for non localized resources" do
|
87
|
+
routes.send(:not_users_path).should == "/not_users"
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should works for localized named_route" do
|
91
|
+
I18n.locale = :fr
|
92
|
+
routes.send(:about_path).should == "/#{I18n.t :about, :scope => :named_routes_path}"
|
93
|
+
I18n.locale = :en
|
94
|
+
routes.send(:about_path).should == "/#{I18n.t :about, :scope => :named_routes_path}"
|
95
|
+
I18n.locale = :de # Not localized
|
96
|
+
routes.send(:about_path).should == "/about"
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should works for localized resource" do
|
100
|
+
I18n.locale = :fr
|
101
|
+
routes.send(:contact_path).should == "/#{I18n.t :contact, :scope => :resource}"
|
102
|
+
I18n.locale = :en
|
103
|
+
routes.send(:contact_path).should == "/#{I18n.t :contact, :scope => :resource}"
|
104
|
+
I18n.locale = :de # Not localized
|
105
|
+
routes.send(:contact_path).should == "/contact"
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should works for localized resources" do
|
109
|
+
I18n.locale = :fr
|
110
|
+
routes.send(:users_path).should == "/#{I18n.t :users, :scope => :resources}"
|
111
|
+
I18n.locale = :en
|
112
|
+
routes.send(:users_path).should == "/#{I18n.t :users, :scope => :resources}"
|
113
|
+
I18n.locale = :de # Not localized
|
114
|
+
routes.send(:users_path).should == "/users"
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should works with url_for" do
|
118
|
+
I18n.locale = :fr
|
119
|
+
url_for(:controller => :users).should == "/#{I18n.t :users, :scope => :resources}"
|
120
|
+
url_for(:controller => :about, :action => :show).should == "/#{I18n.t :about, :scope => :named_routes_path}"
|
121
|
+
I18n.locale = :en
|
122
|
+
url_for(:controller => :users).should == "/#{I18n.t :users, :scope => :resources}"
|
123
|
+
I18n.locale = :de # Not localized
|
124
|
+
url_for(:controller => :users).should == "/users"
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should have correct controller requirements" do
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should main resources works with nested resources" do
|
132
|
+
I18n.locale = :fr
|
133
|
+
routes.send(:authors_path).should == "/#{I18n.t :authors, :scope => :resources}"
|
134
|
+
I18n.locale = :de # Not localized
|
135
|
+
routes.send(:authors_path).should == "/authors"
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should works with nested resources" do
|
139
|
+
I18n.locale = :fr
|
140
|
+
routes.send(:author_books_path, 1).should == "/#{I18n.t :authors, :scope => :resources}/1/#{I18n.t :books, :scope => :resources}"
|
141
|
+
I18n.locale = :de # Not localized
|
142
|
+
routes.send(:author_books_path, 1).should == "/authors/1/books"
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should works with deep nested resources" do
|
146
|
+
I18n.locale = :fr
|
147
|
+
routes.send(:universes_path).should == "/#{I18n.t :universes, :scope => :resources}"
|
148
|
+
routes.send(:universe_galaxies_path, 1).should == "/#{I18n.t :universes, :scope => :resources}/1/#{I18n.t :galaxies, :scope => :resources}"
|
149
|
+
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}"
|
150
|
+
I18n.locale = :de # Not localized
|
151
|
+
routes.send(:universes_path).should == "/universes"
|
152
|
+
routes.send(:universe_galaxies_path, 1).should == "/universes/1/galaxies"
|
153
|
+
routes.send(:universe_galaxy_planets_path, 1, 1).should == "/universes/1/galaxies/1/planets"
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should nested resources have correct significant_keys" do
|
157
|
+
r = ActionController::Routing::Routes.named_routes.instance_eval { @routes }
|
158
|
+
#puts r.keys.to_yaml
|
159
|
+
|
160
|
+
r[:author_fr_books].should_not be_nil
|
161
|
+
|
162
|
+
if Rails.version < '3'
|
163
|
+
r[:author_books].significant_keys.should include(:author_id)
|
164
|
+
r[:author_fr_books].significant_keys.should include(:author_id)
|
165
|
+
else
|
166
|
+
r[:author_books].segment_keys.should include(:author_id)
|
167
|
+
r[:author_fr_books].segment_keys.should include(:author_id)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should deep nested resources have correct significant_keys" do
|
172
|
+
r = ActionController::Routing::Routes.named_routes.instance_eval { @routes }
|
173
|
+
#puts r.keys.to_yaml
|
174
|
+
|
175
|
+
r[:universe_galaxy_fr_planet].should_not be_nil
|
176
|
+
|
177
|
+
if Rails.version < '3'
|
178
|
+
r[:universe_galaxy_planet].significant_keys.should include(:universe_id)
|
179
|
+
r[:universe_galaxy_fr_planet].significant_keys.should include(:universe_id)
|
180
|
+
r[:universe_galaxy_planet].significant_keys.should include(:galaxy_id)
|
181
|
+
r[:universe_galaxy_fr_planet].significant_keys.should include(:galaxy_id)
|
182
|
+
else
|
183
|
+
r[:universe_galaxy_planet].segment_keys.should include(:universe_id)
|
184
|
+
r[:universe_galaxy_fr_planet].segment_keys.should include(:universe_id)
|
185
|
+
r[:universe_galaxy_planet].segment_keys.should include(:galaxy_id)
|
186
|
+
r[:universe_galaxy_fr_planet].segment_keys.should include(:galaxy_id)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should nested resources not deep translate with multi helpers" do
|
191
|
+
r = ActionController::Routing::Routes.named_routes.instance_eval { @routes }
|
192
|
+
|
193
|
+
r.keys.should_not include(:fr_author_books) # Whant fr_author_books
|
194
|
+
end
|
195
|
+
|
196
|
+
# it "zZ Just print routes :)" do
|
197
|
+
# r = ActionController::Routing::Routes.named_routes.instance_eval { @routes }
|
198
|
+
# puts r.keys.collect(&:to_s).sort.to_yaml
|
199
|
+
# puts "Nb Routes : #{r.keys.size}"
|
200
|
+
# end
|
201
|
+
|
202
|
+
end
|
data/spec/locales/en.yml
ADDED
data/spec/locales/fr.yml
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
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 'rack/mount'
|
30
|
+
end
|
31
|
+
|
32
|
+
puts "Launching spec for Rails #{Rails.version}"
|
33
|
+
|
34
|
+
# Add I18n load_path
|
35
|
+
I18n.load_path = (I18n.load_path << Dir[File.join(File.dirname(__FILE__), 'locales', '*.yml')]).uniq
|
36
|
+
|
37
|
+
require File.dirname(__FILE__) + '/../init.rb'
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: i18n_routing
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 1
|
9
|
+
version: 0.2.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Guillaume Luccisano
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-04-09 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
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
|
22
|
+
email: guillaume.luccisano@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- lib/i18n_routing.rb
|
31
|
+
- lib/i18n_routing_rails2.rb
|
32
|
+
- lib/i18n_routing_rails3.rb
|
33
|
+
- spec/i18n_routing/i18n_spec.rb
|
34
|
+
- spec/locales/en.yml
|
35
|
+
- spec/locales/fr.yml
|
36
|
+
- spec/spec_helper.rb
|
37
|
+
- CHANGELOG.rdoc
|
38
|
+
- MIT-LICENSE
|
39
|
+
- Rakefile
|
40
|
+
- README.rdoc
|
41
|
+
- init.rb
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://github.com/kwi/i18n_routing
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
segments:
|
63
|
+
- 1
|
64
|
+
- 3
|
65
|
+
- 4
|
66
|
+
version: 1.3.4
|
67
|
+
requirements: []
|
68
|
+
|
69
|
+
rubyforge_project: i18n_routing
|
70
|
+
rubygems_version: 1.3.6
|
71
|
+
signing_key:
|
72
|
+
specification_version: 3
|
73
|
+
summary: I18n routing module for Rails 2.3.x and Rails 3. Translate your routes with ease !
|
74
|
+
test_files: []
|
75
|
+
|