translate_routes 3.0.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/README.markdown +100 -0
- data/lib/route_translator.rb +306 -0
- data/lib/translate_routes_test_helper.rb +33 -0
- data/test/translate_routes_test.rb +269 -0
- metadata +70 -0
data/README.markdown
ADDED
@@ -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
|