translate_routes 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,100 @@
1
+ TranslateRoutes
2
+ ===============
3
+
4
+ This branch works with Rails 3.x, you can find branches for Rails [2.1.x](http://github.com/raul/translate_routes/tree/rails2.1), [2.2.x](http://github.com/raul/translate_routes/tree/rails2.2) and [2.3.x](http://github.com/raul/translate_routes/tree/rails2.3)
5
+
6
+ This Rails plugin provides a simple way to translate your URLs to any number of languages, even on a fully working application.
7
+
8
+ It works fine with all kind of routing definitions, including RESTful and named routes.
9
+ **Your current code will remain untouched**: your current routing code, helpers and links will be translated transparently - even in your tests.
10
+ (Un)installing it is a very clean and simple process, so why don't you give it a chance? ;)
11
+
12
+ Quick start
13
+ -----------
14
+
15
+ Let's start with a tiny example. Of course you need to define your routes first, e.g:
16
+
17
+ YourApp::Application.routes.draw do
18
+ match 'contact', :to => 'contact#index', :as => 'contact'
19
+ end
20
+
21
+ 1) Download the plugin to your app's `/vendor/plugins` directory.
22
+
23
+ 2) Write your translations on a standard YAML file (e.g: i18n-routes.yml), including the locales and it translations pairs:
24
+
25
+ es:
26
+ contact: contacto
27
+
28
+
29
+ 3) Append a line to your routes.rb file to activate the translations. If you loaded the translations file with
30
+ your other I18n translations files, the line will be:
31
+
32
+ ActionDispatch::Routing::Translator.i18n('es')
33
+
34
+ and if you want to keep the file separated (e.g: config/i18n-routes.yml), the line to append is:
35
+
36
+ ActionDispatch::Routing::Translator.translate_from_file('config','i18n-routes.yml')
37
+
38
+ You can see it working by executing `rake routes` on the shell:
39
+
40
+
41
+ contact_es_es_path /es-ES/contacto {:locale=>"es", :controller=>"contact", :action=>"index"}
42
+ contact_en_us_path /contact {:locale=>"'en'", :controller=>"contact", :action=>"index"}
43
+
44
+
45
+ As we can see, a new spanish route has been setted up and a `locale` parameter has been added to the routes.
46
+
47
+ 4) Include this filter in your ApplicationController:
48
+
49
+ before_filter :set_locale_from_url
50
+
51
+ Now your application recognizes the different routes and sets the `I18n.locale` value on your controllers,
52
+ but what about the routes generation? As you can see on the previous `rake routes` execution, the
53
+ `contact_es_es_path` and `contact_en_us_path` routing helpers have been generated and are
54
+ available in your controllers and views. Additionally, a `contact_path` helper has been generated, which
55
+ generates the routes according to the current request's locale. This way your link
56
+
57
+ This means that if you use named routes **you don't need to modify your application links** because the routing helpers are automatically adapted to the current locale.
58
+
59
+ 5) Hey, but what about my tests?
60
+
61
+ Of course, your functional and integration testing involves some requests.
62
+ The plugin includes some code to add a default locale parameter so they can remain untouched.
63
+ Append it to your `test_helper` and it will be applied.
64
+
65
+ Documentation
66
+ -------------
67
+ You can find additional information in [the translate_routes' wiki](http://wiki.github.com/raul/translate_routes).
68
+
69
+ Questions, suggestions, bug reports...
70
+ --------------------------------------
71
+ Feedback, questions and comments will be always welcome at raul@murciano.net
72
+
73
+ Credits
74
+ -------
75
+ * Main development:
76
+ * Raul Murciano <http://raul.murciano.net> - code
77
+ * Domestika INTERNET S.L <http://domestika.org> - incredible support, really nice people to work with!
78
+
79
+ * Contributors:
80
+ * [Aitor Garay-Romero](http://github.com/aitorgr)
81
+ * [Christian Hølmer](http://github.com/hoelmer)
82
+ * Nico Ritsche
83
+ * [Cedric Darricau](http://github.com/devsigner)
84
+ * [Olivier Gonzalez](http://github.com/gonzoyumo)
85
+ * [Kristian Mandrup](http://github.com/kristianmandrup)
86
+ * [Pieter Visser](http://github.com/pietervisser)
87
+ * [Marian Theisen](http://github.com/cice)
88
+
89
+ Rails routing resources
90
+ -----------------------
91
+ * David Black's 'Rails Routing' ebook rocks! - 'Ruby for Rails' too, BTW.
92
+ * Obie Fernandez's 'The Rails Way' - the definitive RoR reference, great work Obie!
93
+ * As a part of the impressive Rails Guides set there is an [awesome document about rails routing](http://guides.rails.info/routing_outside_in.html) by Mike Gunderloy:
94
+
95
+
96
+ License
97
+ -------
98
+ Copyright (c) 2007 Released under the MIT license (see MIT-LICENSE)
99
+ Raul Murciano <http://raul.murciano.net>
100
+ Domestika INTERNET S.L. <http://domestika.org>
@@ -0,0 +1,306 @@
1
+
2
+ # This class knows nothing
3
+ # about Rails.root or Rails.application.routes, and therefor is easier to
4
+ # test without an Rails App.
5
+ class RouteTranslator
6
+ TRANSLATABLE_SEGMENT = /^(\w+)(\()?/.freeze
7
+ LOCALE_PARAM_KEY = :locale.freeze
8
+ ROUTE_HELPER_CONTAINER = [
9
+ ActionController::Base,
10
+ ActionView::Base,
11
+ ActionMailer::Base,
12
+ ActionDispatch::Routing::UrlFor
13
+ ].freeze
14
+
15
+ # Attributes
16
+
17
+ attr_accessor :dictionary
18
+
19
+ def available_locales
20
+ @available_locales ||= I18n.available_locales.map(&:to_s)
21
+ end
22
+
23
+ def available_locales= locales
24
+ @available_locales = locales.map(&:to_s)
25
+ end
26
+
27
+ def default_locale
28
+ @default_locale ||= I18n.default_locale.to_s
29
+ end
30
+
31
+ def default_locale= locale
32
+ @default_locale = locale.to_s
33
+ end
34
+
35
+ def default_locale? locale
36
+ default_locale == locale.to_s
37
+ end
38
+
39
+
40
+ class << self
41
+ # Default locale suffix generator
42
+ def locale_suffix locale
43
+ locale.to_s.underscore
44
+ end
45
+
46
+ # Creates a RouteTranslator instance, using I18n dictionaries of
47
+ # your app
48
+ def init_with_i18n *wanted_locales
49
+ new.tap do |t|
50
+ t.init_i18n_dictionary *wanted_locales
51
+ end
52
+ end
53
+
54
+ # Creates a RouteTranslator instance and evaluates given block
55
+ # with an empty dictionary
56
+ def init_with_yield &block
57
+ new.tap do |t|
58
+ t.yield_dictionary &block
59
+ end
60
+ end
61
+
62
+ # Creates a RouteTranslator instance and reads the translations
63
+ # from a specified file
64
+ def init_from_file file_path
65
+ new.tap do |t|
66
+ t.load_dictionary_from_file file_path
67
+ end
68
+ end
69
+ end
70
+
71
+ module DictionaryManagement
72
+ # Resets dictionary and yields the block wich can be used to manually fill the dictionary
73
+ # with translations e.g.
74
+ # route_translator = RouteTranslator.new
75
+ # route_translator.yield_dictionary do |dict|
76
+ # dict['en'] = { 'people' => 'people' }
77
+ # dict['de'] = { 'people' => 'personen' }
78
+ # end
79
+ def yield_dictionary &block
80
+ reset_dictionary
81
+ yield @dictionary
82
+ set_available_locales_from_dictionary
83
+ end
84
+
85
+ # Resets dictionary and loads translations from specified file
86
+ # config/locales/routes.yml:
87
+ # en:
88
+ # people: people
89
+ # de:
90
+ # people: personen
91
+ # routes.rb:
92
+ # ... your routes ...
93
+ # ActionDispatch::Routing::Translator.translate_from_file
94
+ # or, to specify a custom file
95
+ # ActionDispatch::Routing::Translator.translate_from_file 'config', 'locales', 'routes.yml'
96
+ def load_dictionary_from_file file_path
97
+ reset_dictionary
98
+ add_dictionary_from_file file_path
99
+ end
100
+
101
+ # Add translations from another file to the dictionary.
102
+ def add_dictionary_from_file file_path
103
+ yaml = YAML.load_file(file_path)
104
+ yaml.each_pair do |locale, translations|
105
+ merge_translations locale, translations
106
+ end
107
+ set_available_locales_from_dictionary
108
+ end
109
+
110
+ # Merge translations for a specified locale into the dictionary
111
+ def merge_translations locale, translations
112
+ locale = locale.to_s
113
+ if translations.blank?
114
+ @dictionary[locale] ||= {}
115
+ return
116
+ end
117
+ @dictionary[locale] = (@dictionary[locale] || {}).merge(translations)
118
+ end
119
+
120
+ # Init dictionary to use I18n to translate route parts. Creates
121
+ # a hash with a block for each locale to lookup keys in I18n dynamically.
122
+ def init_i18n_dictionary *wanted_locales
123
+ wanted_locales = available_locales if wanted_locales.blank?
124
+ reset_dictionary
125
+ wanted_locales.each do |locale|
126
+ @dictionary[locale] = Hash.new do |hsh, key|
127
+ hsh[key] = I18n.translate key, :locale => locale #DISCUSS: caching or no caching (store key and translation in dictionary?)
128
+ end
129
+ end
130
+ @available_locales = @dictionary.keys.map &:to_s
131
+ end
132
+
133
+ private
134
+ def set_available_locales_from_dictionary
135
+ @available_locales = @dictionary.keys.map &:to_s
136
+ end
137
+
138
+ # Resets dictionary
139
+ def reset_dictionary
140
+ @dictionary = { default_locale => {}}
141
+ end
142
+ end
143
+ include DictionaryManagement
144
+
145
+ module Translator
146
+ # Translate a specific RouteSet, usually Rails.application.routes, but can
147
+ # be a RouteSet of a gem, plugin/engine etc.
148
+ def translate route_set
149
+ Rails.logger.info "Translating routes (default locale: #{default_locale})" if defined?(Rails) && defined?(Rails.logger)
150
+
151
+ # save original routes and clear route set
152
+ original_routes = route_set.routes.dup # Array [routeA, routeB, ...]
153
+
154
+ original_named_routes = route_set.named_routes.routes.dup # Hash {:name => :route}
155
+
156
+ reset_route_set route_set
157
+
158
+ original_routes.each do |original_route|
159
+ translations_for(original_route).each do |translated_route_args|
160
+ route_set.add_route *translated_route_args
161
+ end
162
+ end
163
+
164
+ original_named_routes.each_key do |route_name|
165
+ route_set.named_routes.helpers.concat add_untranslated_helpers_to_controllers_and_views(route_name)
166
+ end
167
+
168
+ end
169
+
170
+ # Add unmodified root route to route_set
171
+ def add_root_route root_route, route_set
172
+ root_route.conditions[:path_info] = root_route.conditions[:path_info].dup
173
+ route_set.set.add_route *root_route
174
+ route_set.named_routes[root_route.name] = root_route
175
+ route_set.routes << root_route
176
+ end
177
+
178
+ # Add standard route helpers for default locale e.g.
179
+ # I18n.locale = :de
180
+ # people_path -> people_de_path
181
+ # I18n.locale = :fr
182
+ # people_path -> people_fr_path
183
+ def add_untranslated_helpers_to_controllers_and_views old_name
184
+ ['path', 'url'].map do |suffix|
185
+ new_helper_name = "#{old_name}_#{suffix}"
186
+
187
+ ROUTE_HELPER_CONTAINER.each do |helper_container|
188
+ helper_container.send :define_method, new_helper_name do |*args|
189
+ send "#{old_name}_#{locale_suffix(I18n.locale)}_#{suffix}", *args
190
+ end
191
+ end
192
+
193
+ new_helper_name.to_sym
194
+ end
195
+ end
196
+
197
+ # Generate translations for a single route for all available locales
198
+ def translations_for route
199
+ available_locales.map do |locale|
200
+ translate_route route, locale
201
+ end
202
+ end
203
+
204
+ # Generate translation for a single route for one locale
205
+ def translate_route route, locale
206
+ conditions = { :path_info => translate_path(route.path, locale) }
207
+ conditions[:request_method] = route.conditions[:request_method].source.upcase if route.conditions.has_key? :request_method
208
+ requirements = route.requirements.merge LOCALE_PARAM_KEY => locale
209
+ defaults = route.defaults.merge LOCALE_PARAM_KEY => locale
210
+ new_name = "#{route.name}_#{locale_suffix(locale)}" if route.name
211
+
212
+ [route.app, conditions, requirements, defaults, new_name]
213
+ end
214
+
215
+ # Add prefix for all non-default locales
216
+ def add_prefix? locale
217
+ !default_locale?(locale)
218
+ end
219
+
220
+ # Translates a path and adds the locale prefix.
221
+ def translate_path path, locale
222
+ final_optional_segments = path.match(/(\(.+\))$/)[1] rescue nil # i.e: (.:format)
223
+ path_segments = path.gsub(final_optional_segments,'').split("/")
224
+ new_path = path_segments.map{ |seg| translate_path_segment(seg, locale) }.join('/')
225
+ new_path = "/#{locale}#{new_path}" if add_prefix? locale
226
+ new_path = '/' if new_path.blank?
227
+ final_optional_segments ? new_path + final_optional_segments : new_path
228
+ end
229
+
230
+ # Tries to translate a single path segment. If the path segment
231
+ # contains sth. like a optional format "people(.:format)", only
232
+ # "people" will be translated, if there is no translation, the path
233
+ # segment is blank or begins with a ":" (param key), the segment
234
+ # is returned untouched
235
+ def translate_path_segment segment, locale
236
+ return segment if segment.blank? or segment.starts_with?(":")
237
+
238
+ match = TRANSLATABLE_SEGMENT.match(segment)[1] rescue nil
239
+
240
+ translate_string(match, locale) || segment
241
+ end
242
+
243
+ def translate_string str, locale
244
+ @dictionary[locale.to_s][str.to_s]
245
+ end
246
+
247
+ private
248
+ def reset_route_set route_set
249
+ route_set.clear!
250
+ remove_all_methods_in route_set.named_routes.module
251
+ end
252
+
253
+ def remove_all_methods_in mod
254
+ mod.instance_methods.each do |method|
255
+ mod.send :remove_method, method
256
+ end
257
+ end
258
+ end
259
+ include Translator
260
+
261
+ def locale_suffix locale
262
+ self.class.locale_suffix locale
263
+ end
264
+ end
265
+
266
+ # Adapter for Rails 3 Apps
267
+ module ActionDispatch
268
+ module Routing
269
+ module Translator
270
+ class << self
271
+ def translate &block
272
+ RouteTranslator.init_with_yield(&block).translate Rails.application.routes
273
+ end
274
+
275
+ def translate_from_file *file_path
276
+ file_path = %w(config locales routes.yml) if file_path.blank?
277
+ RouteTranslator.init_from_file(File.join(Rails.root, *file_path)).translate Rails.application.routes
278
+ end
279
+
280
+ def i18n *locales
281
+ RouteTranslator.init_with_i18n(*locales).translate Rails.application.routes
282
+ end
283
+ end
284
+ end
285
+ end
286
+ end
287
+
288
+ # Add set_locale_from_url to controllers
289
+ ActionController::Base.class_eval do
290
+ private
291
+ # called by before_filter
292
+ def set_locale_from_url
293
+ I18n.locale = params[RouteTranslator::LOCALE_PARAM_KEY]
294
+ default_url_options.merge! RouteTranslator::LOCALE_PARAM_KEY => I18n.locale
295
+ end
296
+ end
297
+
298
+ # Add locale_suffix to controllers, views and mailers
299
+ RouteTranslator::ROUTE_HELPER_CONTAINER.each do |klass|
300
+ klass.class_eval do
301
+ private
302
+ def locale_suffix locale
303
+ RouteTranslator.locale_suffix locale
304
+ end
305
+ end
306
+ end
@@ -0,0 +1,33 @@
1
+ # Author: Raul Murciano [http://raul.murciano.net] for Domestika [http://domestika.org]
2
+ # Copyright (c) 2007, Released under the MIT license (see MIT-LICENSE)
3
+
4
+ require 'rails/test_help'
5
+
6
+ # Include default lang on your test requests (test requests doesn't support default_url_options):
7
+ ActionController::TestCase.class_eval do
8
+ unless method_defined?(:process_without_default_language)
9
+ def process_with_default_language(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
10
+ lang_pair = {:locale, I18n.default_locale.to_s}
11
+ parameters = lang_pair.merge(parameters) rescue lang_pair
12
+ process_without_default_language(action, parameters, session, flash, http_method)
13
+ end
14
+
15
+ alias :process_without_default_language :process
16
+ alias :process :process_with_default_language
17
+ end
18
+ end
19
+
20
+ # Add untranslated helper for named routes to integration tests
21
+ ActionController::Integration::Session.class_eval do
22
+ ['path', 'url'].each do |suffix|
23
+ ActionDispatch::Routing::Translator.original_names.each do |old_name|
24
+ new_helper_name = "#{old_name}_#{suffix}"
25
+ def_new_helper = <<-DEF_NEW_HELPER
26
+ def #{new_helper_name}(*args)
27
+ send("#{old_name}_#{ActionDispatch::Routing::Translator.locale_suffix(I18n.locale)}_#{suffix}", *args)
28
+ end
29
+ DEF_NEW_HELPER
30
+ eval def_new_helper
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,269 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'mocha'
4
+
5
+ %w(actionpack activesupport actionmailer).each{ |gem_lib| gem gem_lib, '3.0.1' }
6
+ %w(active_support action_pack action_mailer action_controller action_dispatch).each{ |lib| require lib }
7
+
8
+ plugin_root = File.join(File.dirname(__FILE__), '..')
9
+ require "#{plugin_root}/lib/route_translator"
10
+
11
+
12
+ class PeopleController < ActionController::Base; end
13
+
14
+ class TranslateRoutesTest < ActionController::TestCase
15
+ include ActionDispatch::Assertions::RoutingAssertions
16
+
17
+ def config_default_locale_settings(locale)
18
+ I18n.default_locale = locale
19
+ end
20
+
21
+ def translate_routes
22
+ @route_translator.translate @routes
23
+ @routes.finalize!
24
+ @routes.named_routes.install
25
+ end
26
+
27
+ def setup
28
+ @controller = ActionController::Base.new
29
+ @view = ActionView::Base.new
30
+ @routes = ActionDispatch::Routing::RouteSet.new
31
+ @route_translator = RouteTranslator.new
32
+ end
33
+
34
+ def test_unnamed_root_route
35
+ @routes.draw { root :to => 'people#index' }
36
+ config_default_locale_settings 'en'
37
+ @route_translator.yield_dictionary { |t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
38
+ translate_routes
39
+
40
+ assert_routing '/', :controller => 'people', :action => 'index', :locale => 'en'
41
+ assert_routing '/es', :controller => 'people', :action => 'index', :locale => 'es'
42
+ end
43
+
44
+ def test_unnamed_root_route_without_prefix
45
+ @routes.draw { root :to => 'people#index' }
46
+ config_default_locale_settings 'es'
47
+ @route_translator.load_dictionary_from_file File.expand_path('locales/routes.yml', File.dirname(__FILE__))
48
+ translate_routes
49
+
50
+ assert_routing '/', :controller => 'people', :action => 'index', :locale => 'es'
51
+ assert_routing '/en', :controller => 'people', :action => 'index', :locale => 'en'
52
+ assert_unrecognized_route '/es', :controller => 'people', :action => 'index', :locale => 'es'
53
+ end
54
+
55
+ def test_unnamed_untranslated_route
56
+ @routes.draw { match 'foo', :to => 'people#index' }
57
+ config_default_locale_settings 'en'
58
+ @route_translator.yield_dictionary { |t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
59
+ translate_routes
60
+
61
+ assert_routing '/es/foo', :controller => 'people', :action => 'index', :locale => 'es'
62
+ assert_routing '/foo', :controller => 'people', :action => 'index', :locale => 'en'
63
+ end
64
+
65
+ def test_unnamed_translated_route_on_default_locale
66
+ @routes.draw { match 'people', :to => 'people#index' }
67
+ config_default_locale_settings 'es'
68
+ @route_translator.yield_dictionary { |t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
69
+ translate_routes
70
+
71
+ assert_routing '/en/people', :controller => 'people', :action => 'index', :locale => 'en'
72
+ assert_routing '/gente', :controller => 'people', :action => 'index', :locale => 'es'
73
+ end
74
+
75
+ def test_unnamed_translated_route_on_non_default_locale
76
+ @routes.draw { match 'people', :to => 'people#index' }
77
+ config_default_locale_settings 'en'
78
+ @route_translator.yield_dictionary { |t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
79
+ translate_routes
80
+
81
+ assert_routing '/es/gente', :controller => 'people', :action => 'index', :locale => 'es'
82
+ assert_routing '/people', :controller => 'people', :action => 'index', :locale => 'en'
83
+ end
84
+
85
+ def test_named_translated_route_with_prefix_must_have_locale_as_static_segment
86
+ @routes.draw { match 'people', :to => 'people#index', :as => 'people' }
87
+ config_default_locale_settings 'en'
88
+ @route_translator.yield_dictionary { |t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
89
+ translate_routes
90
+
91
+ # we check the string representation of the route,
92
+ # if it stores locale as a dynamic segment it would be represented as: "/:locale/gente"
93
+ assert_equal "/es/gente(.:format)", path_string(named_route('people_es'))
94
+ end
95
+
96
+ def test_named_empty_route_without_prefix
97
+ @routes.draw { root :to => 'people#index', :as => 'people' }
98
+ config_default_locale_settings 'es'
99
+ @route_translator.yield_dictionary { |t| t['es'] = {}; t['en'] = {'people' => 'gente'}; }
100
+ translate_routes
101
+
102
+ assert_routing '/en', :controller => 'people', :action => 'index', :locale => 'en'
103
+ assert_routing '/', :controller => 'people', :action => 'index', :locale => 'es'
104
+ assert_routing '', :controller => 'people', :action => 'index', :locale => 'es'
105
+ end
106
+
107
+ def test_named_root_route_without_prefix
108
+ @routes.draw { root :to => 'people#index' }
109
+ config_default_locale_settings 'es'
110
+ @route_translator.load_dictionary_from_file File.expand_path('locales/routes.yml', File.dirname(__FILE__))
111
+ translate_routes
112
+
113
+ assert_routing '/', :controller => 'people', :action => 'index', :locale => 'es'
114
+ assert_routing '/en', :controller => 'people', :action => 'index', :locale => 'en'
115
+ assert_unrecognized_route '/es', :controller => 'people', :action => 'index', :locale => 'es'
116
+ end
117
+
118
+ def test_named_untranslated_route_without_prefix
119
+ @routes.draw { match 'foo', :to => 'people#index', :as => 'people' }
120
+ config_default_locale_settings 'es'
121
+ @route_translator.yield_dictionary { |t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
122
+ translate_routes
123
+
124
+ assert_routing '/en/foo', :controller => 'people', :action => 'index', :locale => 'en'
125
+ assert_routing 'foo', :controller => 'people', :action => 'index', :locale => 'es'
126
+ assert_helpers_include :people_en, :people_es, :people
127
+ end
128
+
129
+ def test_named_translated_route_on_default_locale_without_prefix
130
+ @routes.draw { match 'people', :to => 'people#index', :as => 'people'}
131
+ config_default_locale_settings 'es'
132
+ @route_translator.yield_dictionary { |t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
133
+ translate_routes
134
+
135
+ assert_routing '/en/people', :controller => 'people', :action => 'index', :locale => 'en'
136
+ assert_routing 'gente', :controller => 'people', :action => 'index', :locale => 'es'
137
+ assert_helpers_include :people_en, :people_es, :people
138
+ end
139
+
140
+ def test_named_translated_route_on_non_default_locale_without_prefix
141
+ @routes.draw { match 'people', :to => 'people#index', :as => 'people'}
142
+ config_default_locale_settings 'en'
143
+ @route_translator.yield_dictionary { |t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
144
+ translate_routes
145
+
146
+ assert_routing '/people', :controller => 'people', :action => 'index', :locale => 'en'
147
+ assert_routing '/es/gente', :controller => 'people', :action => 'index', :locale => 'es'
148
+ assert_helpers_include :people_en, :people_es, :people
149
+ end
150
+
151
+ def test_formatted_root_route
152
+ @routes.draw{ root :to => 'people#index', :as => 'root' }
153
+ @route_translator.yield_dictionary { |t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
154
+ assert_equal '/(.:format)', path_string(named_route('root'))
155
+ translate_routes
156
+ assert_equal '/(.:format)', path_string(named_route('root_en'))
157
+ assert_equal '/es(.:format)', path_string(named_route('root_es'))
158
+ end
159
+
160
+
161
+ def test_languages_load_from_file
162
+ @routes.draw { match 'people', :to => 'people#index', :as => 'people'}
163
+ config_default_locale_settings 'en'
164
+ @route_translator.load_dictionary_from_file File.expand_path('locales/routes.yml', File.dirname(__FILE__))
165
+ translate_routes
166
+
167
+ assert_routing '/people', :controller => 'people', :action => 'index', :locale => 'en'
168
+ assert_routing '/es/gente', :controller => 'people', :action => 'index', :locale => 'es'
169
+ assert_helpers_include :people_en, :people_es, :people
170
+ end
171
+
172
+ def test_languages_load_from_file_without_dictionary_for_default_locale
173
+ @routes.draw { match 'people', :to => 'people#index', :as => 'people'}
174
+ config_default_locale_settings 'fr'
175
+ @route_translator.load_dictionary_from_file File.expand_path('locales/routes.yml', File.dirname(__FILE__))
176
+ translate_routes
177
+
178
+ assert_routing '/people', :controller => 'people', :action => 'index', :locale => 'fr'
179
+ assert_routing '/en/people', :controller => 'people', :action => 'index', :locale => 'en'
180
+ assert_routing '/es/gente', :controller => 'people', :action => 'index', :locale => 'es'
181
+ assert_helpers_include :people_fr, :people_en, :people_es, :people
182
+ end
183
+
184
+ def test_i18n_based_translations_setting_locales
185
+ @routes.draw { match 'people', :to => 'people#index', :as => 'people'}
186
+ config_default_locale_settings 'en'
187
+ I18n.backend = StubbedI18nBackend
188
+ @route_translator.init_i18n_dictionary 'es'
189
+ translate_routes
190
+
191
+ assert_routing '/es/gente', :controller => 'people', :action => 'index', :locale => 'es'
192
+ assert_routing '/people', :controller => 'people', :action => 'index', :locale => 'en'
193
+ assert_helpers_include :people_en, :people_es, :people
194
+ end
195
+
196
+ def test_i18n_based_translations_taking_i18n_available_locales
197
+ @routes.draw { match 'people', :to => 'people#index', :as => 'people'}
198
+ config_default_locale_settings 'en'
199
+ I18n.stubs(:available_locales).at_least_once.returns StubbedI18nBackend.available_locales
200
+ I18n.backend = StubbedI18nBackend
201
+ @route_translator.init_i18n_dictionary
202
+ translate_routes
203
+
204
+ assert_routing '/fr/people', :controller => 'people', :action => 'index', :locale => 'fr'
205
+ assert_routing '/es/gente', :controller => 'people', :action => 'index', :locale => 'es'
206
+ assert_routing '/people', :controller => 'people', :action => 'index', :locale => 'en'
207
+ assert_helpers_include :people_fr, :people_en, :people_es, :people
208
+ end
209
+
210
+ def test_action_controller_gets_locale_setter
211
+ ActionController::Base.instance_methods.include?('set_locale_from_url')
212
+ end
213
+
214
+ def test_action_controller_gets_locale_suffix_helper
215
+ ActionController::Base.instance_methods.include?('locale_suffix')
216
+ end
217
+
218
+ def test_action_view_gets_locale_suffix_helper
219
+ ActionView::Base.instance_methods.include?('locale_suffix')
220
+ end
221
+
222
+ private
223
+
224
+ # Given a route defined as a string like this:
225
+ # 'ANY /es(.:format) {:controller=>"people", :action=>"index"}'
226
+ # returns "/es(.:format)"
227
+ def path_string(route)
228
+ route.to_s.split(' ')[1]
229
+ end
230
+
231
+ def named_route(name)
232
+ @routes.routes.select{ |r| r.name == name }.first
233
+ end
234
+
235
+ def assert_helpers_include(*helpers)
236
+ helpers.each do |helper|
237
+ ['url', 'path'].each do |suffix|
238
+ [@controller, @view].each { |obj| assert_respond_to obj, "#{helper}_#{suffix}".to_sym }
239
+ end
240
+ end
241
+ end
242
+
243
+ def assert_unrecognized_route(route_path, options)
244
+ assert_raise ActionController::RoutingError do
245
+ assert_routing route_path, options
246
+ end
247
+ end
248
+
249
+ class StubbedI18nBackend
250
+
251
+
252
+ @@translations = {
253
+ 'es' => { 'people' => 'gente'},
254
+ 'fr' => {} # empty on purpose to test behaviour on incompleteness scenarios
255
+ }
256
+
257
+ def self.translate(locale, key, options)
258
+ @@translations[locale.to_s][key] || options[:default]
259
+ rescue
260
+ options[:default]
261
+ end
262
+
263
+ def self.available_locales
264
+ @@translations.keys
265
+ end
266
+
267
+ end
268
+
269
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: translate_routes
3
+ version: !ruby/object:Gem::Version
4
+ hash: 5
5
+ prerelease: false
6
+ segments:
7
+ - 3
8
+ - 0
9
+ - 1
10
+ version: 3.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Raul Murciano
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-11-18 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Translates the Rails routes of your application into the languages defined in your locale files
23
+ email: raul@murciano.net
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.markdown
30
+ files:
31
+ - lib/route_translator.rb
32
+ - lib/translate_routes_test_helper.rb
33
+ - README.markdown
34
+ - test/translate_routes_test.rb
35
+ has_rdoc: true
36
+ homepage: http://github.com/raul/translate_routes
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --charset=UTF-8
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ hash: 3
50
+ segments:
51
+ - 0
52
+ version: "0"
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ requirements: []
63
+
64
+ rubyforge_project:
65
+ rubygems_version: 1.3.7
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: Translate your Rails routes in a simple manner
69
+ test_files:
70
+ - test/translate_routes_test.rb