rails-translate-routes-na 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "activesupport", ">= 3.1"
4
+
5
+ # Add dependencies to develop your gem here.
6
+ # Include everything needed to run rake, tests, features, etc.
7
+ group :development do
8
+ gem "bundler", "~> 1.0.0"
9
+ gem "jeweler", "~> 1.6.4"
10
+ gem 'sqlite3'
11
+ gem "minitest"
12
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,24 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (3.1.3)
5
+ multi_json (~> 1.0)
6
+ git (1.2.5)
7
+ jeweler (1.6.4)
8
+ bundler (~> 1.0)
9
+ git (>= 1.2.5)
10
+ rake
11
+ minitest (2.10.1)
12
+ multi_json (1.0.4)
13
+ rake (0.9.2.2)
14
+ sqlite3 (1.3.6)
15
+
16
+ PLATFORMS
17
+ ruby
18
+
19
+ DEPENDENCIES
20
+ activesupport (>= 3.1)
21
+ bundler (~> 1.0.0)
22
+ jeweler (~> 1.6.4)
23
+ minitest
24
+ sqlite3
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Francesc Pla
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,301 @@
1
+ # NB
2
+
3
+ **This is a fork from the initial https://github.com/raul/rails-translate-routes project**
4
+
5
+ **I renamed the gem to rails-translate-routes-na for easier dependency management within my apps**
6
+
7
+
8
+ # rails-translate-routes
9
+
10
+ **Important change from version 0.0.5 (Feb 2012) to 0.1.0 (June 2012)**: if you're updating from an earlier version take into account that now translations defined in routes.yml are namespaced to avoid conflicts with other translations from app (thanks to cawel for the patch). To upgrade you just have to add the namespace `routes` to your `routes.yml (see example in the below docs).
11
+
12
+ Rails >=3.1 routes translations based on Raul's translate_routes (https://github.com/raul/translate_routes).
13
+
14
+ It's currently a stripped down version of the forked gem, adding some bugfixes for rails 3.1 and features I needed for my project. See doc below to see what it can do.
15
+
16
+ translate_routes & i18n_routes seems to be unmaintained projects so I decided to start a fork of the first one for my own projects, I can't promise high dedication but I'll try to maintain it for my own use and take care all patches & bugs submitted, help is welcome!
17
+
18
+ ## Installation
19
+
20
+ Add it to your Gemfile:
21
+
22
+ gem 'rails-translate-routes'
23
+
24
+ ## Basic usage
25
+
26
+ Let's imagine you have a SampleApp with products and a contact page. A typical `routes.rb` file should look like:
27
+
28
+ SampleApp::Application.routes.draw do
29
+ resources :products
30
+ match 'contact', :to => 'pages#contact'
31
+ end
32
+
33
+ Running `rake routes we have:
34
+
35
+ products GET /products(.:format) {:action=>"index", :controller=>"products"}
36
+ POST /products(.:format) {:action=>"create", :controller=>"products"}
37
+ new_product GET /products/new(.:format) {:action=>"new", :controller=>"products"}
38
+ edit_product GET /products/:id/edit(.:format) {:action=>"edit", :controller=>"products"}
39
+ product GET /products/:id(.:format) {:action=>"show", :controller=>"products"}
40
+ PUT /products/:id(.:format) {:action=>"update", :controller=>"products"}
41
+ DELETE /products/:id(.:format) {:action=>"destroy", :controller=>"products"}
42
+ contact /contact(.:format) {:action=>"contact", :controller=>"pages"}
43
+
44
+ We want to have them in two languages english and spanish, to accomplish this with rails-translate-routes:
45
+
46
+ 1) We have to activate the translations appending this line to the end of `routes.rb
47
+
48
+ ActionDispatch::Routing::Translator.translate_from_file('config/locales/routes.yml')
49
+
50
+ 2) Now we can write translations on a standard YAML file (e.g: in `config/locales/routes.yml`), including all the locales and their translations:
51
+
52
+ en:
53
+ routes:
54
+ # you can leave empty locales, for example the default one
55
+ es:
56
+ routes:
57
+ products: productos
58
+ contact: contacto
59
+ new: crear
60
+
61
+ 3) Include this filter in your `ApplicationController:
62
+
63
+ before_filter :set_locale_from_url
64
+
65
+ Also remember to include language detection to your app, a simple example of an `ApplicationController
66
+
67
+ class ApplicationController < ActionController::Base
68
+ protect_from_forgery
69
+
70
+ before_filter :set_locale
71
+ before_filter :set_locale_from_url
72
+
73
+ private
74
+
75
+ def set_locale
76
+ I18n.locale = params[:locale] || ((lang = request.env['HTTP_ACCEPT_LANGUAGE']) && lang[/^[a-z]{2}/])
77
+ end
78
+ end
79
+
80
+ And that's it! Now if we execute `rake routes`
81
+
82
+ products_en GET /products(.:format) {:action=>"index", :controller=>"products"}
83
+ products_es GET /es/productos(.:format) {:action=>"index", :controller=>"products"}
84
+ POST /products(.:format) {:action=>"create", :controller=>"products"}
85
+ POST /es/productos(.:format) {:action=>"create", :controller=>"products"}
86
+ new_product_en GET /products/new(.:format) {:action=>"new", :controller=>"products"}
87
+ new_product_es GET /es/productos/new(.:format) {:action=>"new", :controller=>"products"}
88
+ edit_product_en GET /products/:id/edit(.:format) {:action=>"edit", :controller=>"products"}
89
+ edit_product_es GET /es/productos/:id/edit(.:format) {:action=>"edit", :controller=>"products"}
90
+ product_en GET /products/:id(.:format) {:action=>"show", :controller=>"products"}
91
+ product_es GET /es/productos/:id(.:format) {:action=>"show", :controller=>"products"}
92
+ PUT /products/:id(.:format) {:action=>"update", :controller=>"products"}
93
+ PUT /es/productos/:id(.:format) {:action=>"update", :controller=>"products"}
94
+ DELETE /products/:id(.:format) {:action=>"destroy", :controller=>"products"}
95
+ DELETE /es/productos/:id(.:format) {:action=>"destroy", :controller=>"products"}
96
+ contact_en /contact(.:format) {:action=>"contact", :controller=>"pages"}
97
+ contact_es /es/contacto(.:format) {:action=>"contact", :controller=>"pages"}
98
+ root_en / {:controller=>"public", :action=>"index"}
99
+ root_es /es {:controller=>"public", :action=>"index"}
100
+
101
+ The application recognizes the different routes and sets the `I18n.locale` value in controllers, but what about the routes generation? As you can see on the previous rake routes execution, the `contact_es_path` and `contact_en_path` routing helpers have been generated and are available in your controllers and views. Additionally, a `contact_path` helper has been generated, which generates the routes according to the current request's locale. 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.
102
+
103
+ ## URL structure options
104
+
105
+ ### Default URL structure
106
+
107
+ By default it generates the following url structure:
108
+
109
+ /
110
+ /es
111
+ /products/
112
+ /es/productos/
113
+ /contact/
114
+ /es/contacto/
115
+
116
+
117
+ ### All languages prefixed
118
+
119
+ In case you want the default languages to be scoped resulting in the following structure:
120
+
121
+ /en
122
+ /es
123
+ /en/products/
124
+ /es/productos/
125
+ /en/contact/
126
+ /es/contacto/
127
+
128
+ You can specify the following option:
129
+
130
+ ActionDispatch::Routing::Translator.translate_from_file('config/locales/routes.yml', { :prefix_on_default_locale => true })
131
+
132
+ If you use the `prefix_on_default_locale` you will have to make the proper redirect on your root controller from http://www.sampleapp.com/ to http://www.sampleapp.com/en or http://www.sampleapp.com/es rails-translate-routes adds an extra unstranslated root path:
133
+
134
+ root_en /en {:controller=>"pages", :action=>"index"}
135
+ root_es /es {:controller=>"pages", :action=>"index"}
136
+ root / {:controller=>"pages", :action=>"index"}
137
+
138
+ A simple example of a redirection to user locale in index method:
139
+
140
+ def index
141
+ unless params[ :locale]
142
+ # it takes I18n.locale from the previous example set_locale as before_filter in application controller
143
+ redirect_to eval("root_#{I18n.locale}_path")
144
+ end
145
+
146
+ # rest of the controller logic ...
147
+ end
148
+
149
+ ### No prefixed languages
150
+
151
+ In case you don't want the language prefix in the url path because you have a domain or subdomain per language (or any other reason). Resulting in this structure:
152
+
153
+ /
154
+ /products/
155
+ /productos/
156
+ /contact/
157
+ /contacto/
158
+
159
+ You can specify the following option:
160
+
161
+ ActionDispatch::Routing::Translator.translate_from_file('config/locales/routes.yml', { :no_prefixes => true })
162
+
163
+ Note that the `no_prefixes` option will override the `prefix_on_default_locale` option.
164
+
165
+ ### Keep untranslated routes
166
+
167
+ In case you want to keep access to untranslated routes, for easier api or ajax integration for example. Resulting in this structure:
168
+
169
+ /en/available-products
170
+ /fr/produits-disponibles/
171
+ /products/
172
+
173
+ You can specify the following option:
174
+
175
+ ActionDispatch::Routing::Translator.translate_from_file('config/locales/routes.yml', { :keep_untranslated_routes => true })
176
+
177
+ This option is not meant to be used with `no_prefixes`.
178
+
179
+ ### Namespaced backends
180
+
181
+ I usually build app backend in namespaced controllers, routes, ... using translated routes will result in duplicated routes or prefixed ones. In most cases you won't want to have the backend in several languages, you can set `routes.rb` this way:
182
+
183
+ SampleApp::Application.routes.draw do
184
+ resources :products
185
+ match 'contact', :to => 'pages#contact'
186
+ end
187
+
188
+ ActionDispatch::Routing::Translator.translate_from_file('config/locales/routes.yml', { :prefix_on_default_locale => true })
189
+
190
+ SampleApp::Application.routes.draw do
191
+ namespace :admin do
192
+ resources :products
193
+ root :to => "admin_products#index"
194
+ end
195
+ end
196
+
197
+ ### Translation of routes inside engines
198
+
199
+ If you need to translate routes inside a mounted engine, like the example below:
200
+
201
+ MyEngine::Engine.routes.draw do
202
+ match "route_to_translate" => "controller#action", :as => :to_translate
203
+ end
204
+
205
+ Rails.application.routes.draw do
206
+ match 'untranslated' => ...
207
+
208
+ mount MyEngine::Engine => "/my_engine"
209
+ end
210
+
211
+ # You want to get routes like this
212
+ # ---------------------------------
213
+ untranslated /untranslated(.:format) home#index
214
+ my_engine /my_engine MyEngine::Engine
215
+
216
+ Routes for MyEngine::Engine:
217
+ to_translate_en /en/translated(.:format) my_engine/controller#action {:locale=>"en"}
218
+ to_translate_fr /fr/traduit(.:format) my_engine/controller#action {:locale=>"fr"}
219
+
220
+ All you need to do is to add rails-translate-routes to your engine gemspec and use the option :custom_route_set as follows
221
+
222
+ # inside your engine/config/routes
223
+ ActionDispatch::Routing::Translator.translate_from_file('config/locales/routes.yml', { :custom_route_set => MyEngine::Engine.routes })
224
+
225
+ ## Translation YAML options
226
+
227
+ By default translations of routes are specified on a single YAML file (e.g: in `config/locales/routes.yml`), but in some cases you might want to split the routes translations on several files. For example having all application translations files a in folder per language:
228
+
229
+ # config/locales/en
230
+ rails_defaults.yml
231
+ application.yml
232
+ routes.yml
233
+
234
+ # config/locales/es
235
+ rails_defaults.yml
236
+ application.yml
237
+ routes.yml
238
+
239
+ To pass several YAML files to rails-translate-routes you can pass an array of paths:
240
+
241
+ ActionDispatch::Routing::Translator.translate_from_file(I18n.available_locales.map { |locale| "config/locales/#{locale}/routes.yml" }, { :prefix_on_default_locale => true })
242
+
243
+ ## Testing
244
+
245
+ Once your app is locale-aware, the routes are dependent on the locale. This means that in functional tests, you need to explicitly include the locale like so:
246
+
247
+ get :show, :id => 1, :locale => 'en'
248
+
249
+ But in case you would prefer the locale to be implicit or simply because you don't want to add the locale param to all your previous functional tests, you can require the `lib/controller_test_helper` file (typically from your test helper file) and then this will work fine:
250
+
251
+ get :show, :id => 1
252
+
253
+ ## TODO
254
+
255
+ Help is welcome ;)
256
+
257
+ * make basic testing
258
+
259
+ ## Credits
260
+
261
+ Thanks to:
262
+
263
+ Contributors of the current gem:
264
+ * Martin Carel (https://github.com/cawel)
265
+ * Johan Gyllenspetz (https://github.com/gyllen)
266
+ * Nico Ritsche (https://github.com/ncri)
267
+ * Jean-Loup Fenaux (https://github.com/jlfenaux)
268
+ * Cyril Mouge (https://github.com/shingara)
269
+ * Nicolas Arbogast (https://github.com/NicoArbogast)
270
+ * Stephan van Eijkelenburg (https://github.com/stephanvane)
271
+ * Víctor Martín (https://github.com/eltercero)
272
+
273
+ Main development of forked gem:
274
+ * Raul Murciano (http://github.com/raul)
275
+
276
+ Contributors of forked gem:
277
+ * Aitor Garay-Romero (http://github.com/aitorgr)
278
+ * Christian Hølmer (http://github.com/hoelmer)
279
+ * Nico Ritsche (https://github.com/ncri)
280
+ * Cedric Darricau (http://github.com/devsigner)
281
+ * Olivier Gonzalez (http://github.com/gonzoyumo)
282
+ * Kristian Mandrup (http://github.com/kristianmandrup)
283
+ * Pieter Visser (http://github.com/pietervisser)
284
+ * Marian Theisen (http://github.com/cice)
285
+ * Enric Lluelles (http://github.com/enriclluelles)
286
+ * Jaime Iniesta (http://github.com/jaimeiniesta)
287
+
288
+ ## Similar projects
289
+
290
+ Another fork of translate_routes, a much cleaner approach with better testing but less flexible than rails-translate-routes:
291
+
292
+ * route_translator (https://github.com/enriclluelles/route_translator)
293
+
294
+ Also there are other two projects for translating routes in Rails (which I know of), both of them are unfortunately unmaintained but you may want to check them out if you use old Rails versions or have different needs.
295
+
296
+ * translate_routes (https://github.com/raul/translate_routes)
297
+ * i18n_routing (https://github.com/kwi/i18n_routing)
298
+
299
+ ## Other i18n related projects
300
+
301
+ If you also need to translate models check out: https://github.com/francesc/rails-translate-models
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "rails-translate-routes-na"
18
+ gem.homepage = "http://github.com/francesc/rails-translate-routes-na"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Simple gem to translate routes in Rails 3.x}
21
+ gem.description = %Q{Simple gem to translate routes in Rails 3.x based on translate_routes}
22
+ gem.email = "francesc@francesc.net"
23
+ gem.authors = ["Francesc Pla"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ task :default => :test
36
+
37
+ require 'rdoc/task'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "rails-translate-routes-na #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,19 @@
1
+ # This monkey patch injects the locale in the controller's params
2
+ # Reason: ActionController::TestCase doesn't support "default_url_options"
3
+ #
4
+ # Example: when the request you are testing needs the locale, e.g.
5
+ # get :show, :id => 1, :locale => 'en'
6
+ #
7
+ # if the monkey patch was loaded, you can just do:
8
+ # get :show, :id => 1
9
+
10
+ class ActionController::TestCase
11
+
12
+ module Behavior
13
+ def process_with_default_locale(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
14
+ parameters = { :locale => I18n.default_locale.to_s }.merge(parameters || {} )
15
+ process_without_default_locale(action, parameters, session, flash, http_method)
16
+ end
17
+ alias_method_chain :process, :default_locale
18
+ end
19
+ end
@@ -0,0 +1,462 @@
1
+ # coding: UTF-8
2
+
3
+ # This class knows nothing about Rails.root or Rails.application.routes,
4
+ # and therefore is easier to test without a Rails app.
5
+ class RailsTranslateRoutes
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
+ def prefix_on_default_locale
40
+ @prefix_on_default_locale ||= I18n.default_locale.to_s
41
+ end
42
+
43
+ def prefix_on_default_locale= locale
44
+ @prefix_on_default_locale = locale.to_s
45
+ end
46
+
47
+ def no_prefixes
48
+ @no_prefixes ||= false
49
+ end
50
+
51
+ def no_prefixes= no_prefixes
52
+ @no_prefixes = no_prefixes
53
+ end
54
+
55
+ # option allowing to keep untranslated routes
56
+ # *Ex:
57
+ # *resources :users
58
+ # *translated routes
59
+ # en/members
60
+ # fr/membres
61
+ # /users
62
+ def keep_untranslated_routes
63
+ @keep_untranslated_routes ||= false
64
+ end
65
+
66
+ def keep_untranslated_routes= keep_untranslated_routes
67
+ @keep_untranslated_routes = keep_untranslated_routes
68
+ end
69
+
70
+ def custom_route_set
71
+ @custom_route_set ||= Rails.application.routes
72
+ end
73
+
74
+ def custom_route_set= custom_route_set
75
+ @custom_route_set = custom_route_set
76
+ end
77
+
78
+ class << self
79
+ # Default locale suffix generator
80
+ def locale_suffix locale
81
+ locale.to_s.underscore
82
+ end
83
+
84
+ # Creates a RailsTranslateRoutes instance, using I18n dictionaries of
85
+ # your app
86
+ def init_with_i18n *wanted_locales
87
+ new.tap do |t|
88
+ t.init_i18n_dictionary *wanted_locales
89
+ end
90
+ end
91
+
92
+ # Creates a RailsTranslateRoutes instance and evaluates given block
93
+ # with an empty dictionary
94
+ def init_with_yield &block
95
+ new.tap do |t|
96
+ t.yield_dictionary &block
97
+ end
98
+ end
99
+
100
+ # Creates a RailsTranslateRoutes instance and reads the translations
101
+ # from a specified file
102
+ def init_from_file file_path
103
+ new.tap do |t|
104
+ t.load_dictionary_from_file file_path
105
+ end
106
+ end
107
+ end
108
+
109
+ module DictionaryManagement
110
+ # Resets dictionary and yields the block wich can be used to manually fill the dictionary
111
+ # with translations e.g.
112
+ # route_translator = RailsTranslateRoutes.new
113
+ # route_translator.yield_dictionary do |dict|
114
+ # dict['en'] = { 'people' => 'people' }
115
+ # dict['de'] = { 'people' => 'personen' }
116
+ # end
117
+ def yield_dictionary &block
118
+ reset_dictionary
119
+ yield @dictionary
120
+ set_available_locales_from_dictionary
121
+ end
122
+
123
+ # Resets dictionary and loads translations from specified file
124
+ # config/locales/routes.yml:
125
+ # en:
126
+ # people: people
127
+ # de:
128
+ # people: personen
129
+ # routes.rb:
130
+ # ... your routes ...
131
+ # ActionDispatch::Routing::Translator.translate_from_file
132
+ # or, to specify a custom file
133
+ # ActionDispatch::Routing::Translator.translate_from_file 'config', 'locales', 'routes.yml'
134
+ def load_dictionary_from_file file_path
135
+ reset_dictionary
136
+ add_dictionary_from_file file_path
137
+ end
138
+
139
+ # Add translations from another file to the dictionary (supports a single file or an array to have translations splited in several folders)
140
+ def add_dictionary_from_file file_path
141
+ file_path.class == Array ? files = file_path : files = [ file_path ]
142
+
143
+ yaml = Hash.new
144
+ files.each do |file|
145
+ yaml.merge! YAML.load_file(File.join(Rails.root, file))
146
+ end
147
+
148
+ yaml.each_pair do |locale, translations|
149
+ merge_translations locale, translations['routes']
150
+ end
151
+
152
+ set_available_locales_from_dictionary
153
+ end
154
+
155
+ # Merge translations for a specified locale into the dictionary
156
+ def merge_translations locale, translations
157
+ locale = locale.to_s
158
+ if translations.blank?
159
+ @dictionary[locale] ||= {}
160
+ return
161
+ end
162
+ @dictionary[locale] = (@dictionary[locale] || {}).merge(translations)
163
+ end
164
+
165
+ # Init dictionary to use I18n to translate route parts. Creates
166
+ # a hash with a block for each locale to lookup keys in I18n dynamically.
167
+ def init_i18n_dictionary *wanted_locales
168
+ wanted_locales = available_locales if wanted_locales.blank?
169
+ reset_dictionary
170
+ wanted_locales.each do |locale|
171
+ @dictionary[locale] = Hash.new do |hsh, key|
172
+ hsh[key] = I18n.translate key, :locale => locale #DISCUSS: caching or no caching (store key and translation in dictionary?)
173
+ end
174
+ end
175
+ @available_locales = @dictionary.keys.map &:to_s
176
+ end
177
+
178
+ private
179
+ def set_available_locales_from_dictionary
180
+ @available_locales = @dictionary.keys.map &:to_s
181
+ end
182
+
183
+ # Resets dictionary
184
+ def reset_dictionary
185
+ @dictionary = { default_locale => {}}
186
+ end
187
+ end
188
+ include DictionaryManagement
189
+
190
+ module Translator
191
+ # Translate a specific RouteSet, usually Rails.application.routes, but can
192
+ # be a RouteSet of a gem, plugin/engine etc.
193
+ def translate route_set
194
+ route_set ||= custom_route_set
195
+ Rails.logger.info "Translating routes (default locale: #{default_locale})" if defined?(Rails) && defined?(Rails.logger)
196
+
197
+ # save original routes and clear route set
198
+ original_routes = route_set.routes.dup # Array [routeA, routeB, ...]
199
+
200
+ if Rails.version >= '3.2'
201
+ original_routes.routes.delete_if{|r| r.path.spec.to_s == '/assets' }
202
+ else
203
+ original_routes.delete_if{|r| r.path == '/assets'}
204
+ end
205
+
206
+ original_named_routes = route_set.named_routes.routes.dup # Hash {:name => :route}
207
+
208
+ if Rails.version >= '3.2'
209
+ translated_routes = []
210
+ original_routes.each do |original_route|
211
+ translations_for(original_route).each do |translated_route_args|
212
+ translated_routes << translated_route_args
213
+ end
214
+ end
215
+
216
+ reset_route_set route_set
217
+
218
+ translated_routes.each do |translated_route_args|
219
+ route_set.add_route *translated_route_args
220
+ end
221
+ else
222
+ reset_route_set route_set
223
+
224
+ original_routes.each do |original_route|
225
+ translations_for(original_route).each do |translated_route_args|
226
+ route_set.add_route *translated_route_args
227
+ end
228
+ end
229
+ end
230
+
231
+ original_named_routes.each_key do |route_name|
232
+ route_set.named_routes.helpers.concat add_untranslated_helpers_to_controllers_and_views(route_name)
233
+ end
234
+
235
+ if root_route = original_named_routes[:root]
236
+ add_root_route root_route, route_set
237
+ end
238
+
239
+ end
240
+
241
+ # Add unmodified root route to route_set
242
+ def add_root_route root_route, route_set
243
+ if @prefix_on_default_locale
244
+ if Rails.version >= '3.2'
245
+ conditions = { :path_info => root_route.path.spec.to_s }
246
+ conditions[:request_method] = parse_request_methods root_route.verb if root_route.verb != //
247
+ route_set.add_route root_route.app, conditions, root_route.requirements, root_route.defaults, root_route.name
248
+ else
249
+ root_route.conditions[:path_info] = root_route.conditions[:path_info].dup
250
+ route_set.set.add_route *root_route
251
+ route_set.named_routes[root_route.name] = root_route
252
+ route_set.routes << root_route
253
+ end
254
+ end
255
+ end
256
+
257
+ # Add standard route helpers for default locale e.g.
258
+ # I18n.locale = :de
259
+ # people_path -> people_de_path
260
+ # I18n.locale = :fr
261
+ # people_path -> people_fr_path
262
+ def add_untranslated_helpers_to_controllers_and_views old_name
263
+ ['path', 'url'].map do |suffix|
264
+ new_helper_name = "#{old_name}_#{suffix}"
265
+
266
+ ROUTE_HELPER_CONTAINER.each do |helper_container|
267
+ helper_container.send :define_method, new_helper_name do |*args|
268
+ send "#{old_name}_#{locale_suffix(I18n.locale)}_#{suffix}", *args
269
+ end
270
+
271
+ # !!! very dirty hack to ensure it works inside Engines
272
+ #
273
+ helper_container.send :define_method, :method_missing do |*args|
274
+ metod = args.shift
275
+ if metod[/_path/] || metod[/_url/] and self.class != helper_container
276
+ self.class.send :define_method, metod do |*arguments|
277
+ locale = arguments.collect{|arg| arg.is_a?(Hash)}.first.try(:[], :locale)
278
+ send metod.to_s.gsub(/path$/, "#{locale || I18n.locale}_path").gsub(/url$/, "#{locale || I18n.locale}_url"), *arguments
279
+ end
280
+ send *args.unshift(metod)
281
+ else
282
+ super *args.unshift(metod)
283
+ end
284
+ end
285
+ end
286
+
287
+ new_helper_name.to_sym
288
+ end
289
+ end
290
+
291
+ # Generate translations for a single route for all available locales
292
+ def translations_for route
293
+ translated_routes = []
294
+ available_locales.map do |locale|
295
+ translated_routes << translate_route(route, locale)
296
+ end
297
+
298
+ # add untranslated_route without url helper if we want to keep untranslated routes
299
+ translated_routes << untranslated_route(route) if @keep_untranslated_routes
300
+ translated_routes
301
+ end
302
+
303
+ # Generate translation for a single route for one locale
304
+ def translate_route route, locale
305
+ if Rails.version >= '3.2'
306
+ conditions = { :path_info => translate_path(route.path.spec.to_s, locale) }
307
+ conditions[:request_method] = parse_request_methods route.verb if route.verb != //
308
+ if route.constraints
309
+ route.constraints.each do |k,v|
310
+ conditions[k] = v unless k == :request_method
311
+ end
312
+ end
313
+ defaults = route.defaults.merge LOCALE_PARAM_KEY => locale.dup
314
+ else
315
+ conditions = { :path_info => translate_path(route.path, locale) }
316
+ conditions[:request_method] = parse_request_methods route.conditions[:request_method] if route.conditions.has_key? :request_method
317
+ defaults = route.defaults.merge LOCALE_PARAM_KEY => locale
318
+ end
319
+
320
+ requirements = route.requirements.merge LOCALE_PARAM_KEY => locale
321
+ new_name = "#{route.name}_#{locale_suffix(locale)}" if route.name
322
+
323
+ [route.app, conditions, requirements, defaults, new_name]
324
+ end
325
+
326
+ # Re-generate untranslated routes (original routes) with name set to nil (which prevents conflict with default untranslated_urls)
327
+ def untranslated_route route
328
+ conditions = {}
329
+ if Rails.version >= '3.2'
330
+ conditions[:path_info] = route.path.spec.to_s
331
+ conditions[:request_method] = parse_request_methods route.verb if route.verb != //
332
+ conditions[:subdomain] = route.constraints[:subdomain] if route.constraints
333
+ else
334
+ conditions[:path_info] = route.path
335
+ conditions[:request_method] = parse_request_methods route.conditions[:request_method] if route.conditions.has_key? :request_method
336
+ end
337
+ requirements = route.requirements
338
+ defaults = route.defaults
339
+
340
+ [route.app, conditions, requirements, defaults]
341
+ end
342
+
343
+ # Add prefix for all non-default locales
344
+ def add_prefix? locale
345
+ if @no_prefixes
346
+ false
347
+ elsif !default_locale?(locale) || @prefix_on_default_locale
348
+ true
349
+ else
350
+ false
351
+ end
352
+ end
353
+
354
+ # Translates a path and adds the locale prefix.
355
+ def translate_path path, locale
356
+ new_path = split_translate_join path, locale, [')','(','/'], false
357
+ new_path = split_translate_join path, locale, [')','(','/']
358
+ new_path = "/:locale#{new_path || '/'}" if add_prefix?(locale)
359
+ new_path = '/' if new_path.blank?
360
+ new_path
361
+ end
362
+
363
+ def split_translate_join(segment, locale, separators, translate = true)
364
+ separator = separators[0]
365
+ final_optional_segments = segment[Regexp.new("(#{'\\' + separator })$")] || ""
366
+ segment.split(separator).map do |s|
367
+ if separators[1..-1].any?
368
+ split_translate_join(s, locale, separators[1..-1], translate)
369
+ else
370
+ translate ? translate_path_segment(s,locale) : s
371
+ end
372
+ end.join(separator) + (final_optional_segments != "/" ? final_optional_segments : '')
373
+ end
374
+
375
+ # Tries to translate a single path segment. If the path segment
376
+ # contains sth. like an optional format "people(.:format)", only
377
+ # "people" will be translated, if there is no translation, the path
378
+ # segment is blank or begins with a ":" (param key), the segment
379
+ # is returned untouched
380
+ def translate_path_segment segment, locale
381
+ return segment if segment.blank? or segment.starts_with?(":")
382
+
383
+ match = TRANSLATABLE_SEGMENT.match(segment)[1] rescue nil
384
+
385
+ (translate_string(match, locale) || segment)
386
+ end
387
+
388
+ def translate_string str, locale
389
+ @dictionary[locale.to_s][str.to_s]
390
+ end
391
+
392
+ private
393
+ def reset_route_set route_set
394
+ route_set.clear!
395
+ remove_all_methods_in route_set.named_routes.module
396
+ end
397
+
398
+ def remove_all_methods_in mod
399
+ mod.instance_methods.each do |method|
400
+ mod.send :remove_method, method
401
+ end
402
+ end
403
+
404
+ # expects methods regexp to be in a format: /^GET$/ or /^GET|POST$/ and returns array ["GET", "POST"]
405
+ def parse_request_methods methods_regexp
406
+ methods_regexp.source.gsub(/\^([a-zA-Z\|]+)\$/, "\\1").split("|")
407
+ end
408
+ end
409
+ include Translator
410
+
411
+ def locale_suffix locale
412
+ self.class.locale_suffix locale
413
+ end
414
+ end
415
+
416
+ # Adapter for Rails 3 apps
417
+ module ActionDispatch
418
+ module Routing
419
+ module Translator
420
+ class << self
421
+ def translate(route_set = nil, &block)
422
+ route_set ||= custom_route_set
423
+ RailsTranslateRoutes.init_with_yield(&block).translate route_set
424
+ end
425
+
426
+ def translate_from_file(file_path, options = {})
427
+ file_path = %w(config locales routes.yml) if file_path.blank?
428
+ r = RailsTranslateRoutes.init_from_file(file_path)
429
+ r.prefix_on_default_locale = true if options && options[:prefix_on_default_locale] == true
430
+ r.no_prefixes = true if options && options[:no_prefixes] == true
431
+ r.keep_untranslated_routes = true if options && options[:keep_untranslated_routes] == true
432
+ r.custom_route_set = options[:custom_route_set] if options
433
+ r.translate r.custom_route_set
434
+ end
435
+
436
+ def i18n *locales
437
+ RailsTranslateRoutes.init_with_i18n(*locales).translate
438
+ end
439
+ end
440
+ end
441
+ end
442
+ end
443
+
444
+ # Add set_locale_from_url to controllers
445
+ ActionController::Base.class_eval do
446
+ private
447
+ # called by before_filter
448
+ def set_locale_from_url
449
+ I18n.locale = params[RailsTranslateRoutes::LOCALE_PARAM_KEY]
450
+ default_url_options = {RailsTranslateRoutes::LOCALE_PARAM_KEY => I18n.locale}
451
+ end
452
+ end
453
+
454
+ # Add locale_suffix to controllers, views and mailers
455
+ RailsTranslateRoutes::ROUTE_HELPER_CONTAINER.each do |klass|
456
+ klass.class_eval do
457
+ private
458
+ def locale_suffix locale
459
+ RailsTranslateRoutes.locale_suffix locale
460
+ end
461
+ end
462
+ end
@@ -0,0 +1,63 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "rails-translate-routes-na"
8
+ s.version = File.open('./VERSION').read
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Francesc Pla"]
12
+ s.date = "2012-08-03"
13
+ s.description = "Simple gem to translate routes in Rails 3.x based on translate_routes"
14
+ s.email = "francesc@francesc.net"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.md",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "lib/controller_test_helper.rb",
28
+ "lib/rails-translate-routes.rb",
29
+ "rails-translate-routes-na.gemspec",
30
+ "test/helper.rb",
31
+ "test/test_rails-translate-routes.rb"
32
+ ]
33
+ s.homepage = "http://github.com/francesc/rails-translate-routes-na"
34
+ s.licenses = ["MIT"]
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = "1.8.24"
37
+ s.summary = "Simple gem to translate routes in Rails 3.x"
38
+
39
+ if s.respond_to? :specification_version then
40
+ s.specification_version = 3
41
+
42
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
43
+ s.add_runtime_dependency(%q<activesupport>, [">= 3.1"])
44
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
45
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
46
+ s.add_development_dependency(%q<sqlite3>, [">= 0"])
47
+ s.add_development_dependency(%q<minitest>, [">= 0"])
48
+ else
49
+ s.add_dependency(%q<activesupport>, [">= 3.1"])
50
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
51
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
52
+ s.add_dependency(%q<sqlite3>, [">= 0"])
53
+ s.add_dependency(%q<minitest>, [">= 0"])
54
+ end
55
+ else
56
+ s.add_dependency(%q<activesupport>, [">= 3.1"])
57
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
58
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
59
+ s.add_dependency(%q<sqlite3>, [">= 0"])
60
+ s.add_dependency(%q<minitest>, [">= 0"])
61
+ end
62
+ end
63
+
data/test/helper.rb ADDED
@@ -0,0 +1,17 @@
1
+ # coding: UTF-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+
13
+ require 'minitest/autorun'
14
+
15
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
16
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
17
+ require 'rails-translate-routes'
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestRailsTranslateRoutes < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails-translate-routes-na
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Francesc Pla
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: &70364424245020 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '3.1'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70364424245020
25
+ - !ruby/object:Gem::Dependency
26
+ name: bundler
27
+ requirement: &70364424243760 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70364424243760
36
+ - !ruby/object:Gem::Dependency
37
+ name: jeweler
38
+ requirement: &70364424258160 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 1.6.4
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70364424258160
47
+ - !ruby/object:Gem::Dependency
48
+ name: sqlite3
49
+ requirement: &70364424255200 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70364424255200
58
+ - !ruby/object:Gem::Dependency
59
+ name: minitest
60
+ requirement: &70364424267680 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70364424267680
69
+ description: Simple gem to translate routes in Rails 3.x based on translate_routes
70
+ email: francesc@francesc.net
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files:
74
+ - LICENSE.txt
75
+ - README.md
76
+ files:
77
+ - .document
78
+ - Gemfile
79
+ - Gemfile.lock
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - VERSION
84
+ - lib/controller_test_helper.rb
85
+ - lib/rails-translate-routes.rb
86
+ - rails-translate-routes-na.gemspec
87
+ - test/helper.rb
88
+ - test/test_rails-translate-routes.rb
89
+ homepage: http://github.com/francesc/rails-translate-routes-na
90
+ licenses:
91
+ - MIT
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 1.8.17
111
+ signing_key:
112
+ specification_version: 3
113
+ summary: Simple gem to translate routes in Rails 3.x
114
+ test_files: []