translate_routes 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|