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 +55 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +28 -0
- data/Rakefile +28 -0
- data/init.rb +2 -0
- data/lib/i18n_routing.rb +8 -0
- data/lib/i18n_routing_common.rb +50 -0
- data/lib/i18n_routing_rails2.rb +239 -0
- data/lib/i18n_routing_rails3.rb +376 -0
- data/spec/i18n_routing/i18n_spec.rb +309 -0
- data/spec/locales/en.yml +13 -0
- data/spec/locales/fr.yml +30 -0
- data/spec/spec_helper.rb +42 -0
- metadata +97 -0
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
data/lib/i18n_routing.rb
ADDED
@@ -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
|
data/spec/locales/en.yml
ADDED
@@ -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'
|
data/spec/locales/fr.yml
ADDED
@@ -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'
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
+
|