route_translator 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ script: "bundle exec rake test"
2
+ rvm:
3
+ - 1.9.3
4
+ gemfile:
5
+ - gemfiles/Gemfile.rails-3.0.x
6
+ - gemfiles/Gemfile.rails-3.1.x
7
+ - Gemfile
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "rails", "~> 3.2.6"
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Raul Murciano [http://raul.murciano.net], Domestika INTERNET S.L. [http://domestika.org], 2012 Enric Lluelles [http://enric.lluell.es]
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,128 @@
1
+ RouteTranslator
2
+ ===============
3
+
4
+ [![Build Status](https://secure.travis-ci.org/enriclluelles/route_translator.png)](http://travis-ci.org/enriclluelles/route_translator)
5
+
6
+ RouteTranslator is a gem to allow you to manage the translations of your
7
+ app routes with a simple dictionary format
8
+
9
+ It started as a fork of the awesome [translate_routes](https://github.com/raul/translate_routes) plugin by [Raúl Murciano](https://github.com/raul) and then I made changes as I needed until it became the actual code
10
+
11
+ Right now it works with all the different flavous of rails3 (3.0, 3.1, 3.2) but I'm planning to make it compatible with rails 2.3 too. I'll see how it goes
12
+
13
+
14
+ Quick Start
15
+ -----------
16
+
17
+ 1. If you have this `routes.rb` file originally:
18
+
19
+ ```ruby
20
+ MyApp::Application.routes.draw do
21
+
22
+ namespace :admin do
23
+ resources :cars
24
+ end
25
+
26
+ resources :cars
27
+ end
28
+ ```
29
+
30
+ the output of `rake routes.rb` would be this:
31
+
32
+ ```
33
+ admin_cars GET /admin/cars(.:format) admin/cars#index
34
+ POST /admin/cars(.:format) admin/cars#create
35
+ new_admin_car GET /admin/cars/new(.:format) admin/cars#new
36
+ edit_admin_car GET /admin/cars/:id/edit(.:format) admin/cars#edit
37
+ admin_car GET /admin/cars/:id(.:format) admin/cars#show
38
+ PUT /admin/cars/:id(.:format) admin/cars#update
39
+ DELETE /admin/cars/:id(.:format) admin/cars#destroy
40
+ cars GET /cars(.:format) cars#index
41
+ POST /cars(.:format) cars#create
42
+ new_car GET /cars/new(.:format) cars#new
43
+ edit_car GET /cars/:id/edit(.:format) cars#edit
44
+ car GET /cars/:id(.:format) cars#show
45
+ PUT /cars/:id(.:format) cars#update
46
+ DELETE /cars/:id(.:format) cars#destroy
47
+ ```
48
+
49
+ 2. Add the gem to your `Gemfile`:
50
+
51
+ ```ruby
52
+ gem 'route_translator'
53
+ ```
54
+
55
+ And execute `bundle install`
56
+
57
+ 3. Wrap the groups of routes that you want to translate inside a
58
+ `localized` block:
59
+
60
+ ```ruby
61
+ MyApp::Application.routes.draw do
62
+
63
+ namespace :admin do
64
+ resources :cars
65
+ end
66
+
67
+ localized do
68
+ resources :cars
69
+ end
70
+ end
71
+
72
+ MyApp::Application.routes.translate_from_file #you can pass the file path as a param here
73
+ #the deault is config/i18n-routes.yml
74
+ ```
75
+
76
+ And add the translations to a YAML file, for example
77
+ `config/i18n-routes.yml`:
78
+
79
+ ```yaml
80
+ es:
81
+ cars: coches
82
+ new: nuevo
83
+ fr:
84
+ cars: voitures
85
+ new: nouveau
86
+ ```
87
+
88
+ 4. Your routes are translated! Here's the output of your `rake routes` now:
89
+
90
+ ```
91
+ admin_cars GET /admin/cars(.:format) admin/cars#index
92
+ POST /admin/cars(.:format) admin/cars#create
93
+ new_admin_car GET /admin/cars/new(.:format) admin/cars#new
94
+ edit_admin_car GET /admin/cars/:id/edit(.:format) admin/cars#edit
95
+ admin_car GET /admin/cars/:id(.:format) admin/cars#show
96
+ PUT /admin/cars/:id(.:format) admin/cars#update
97
+ DELETE /admin/cars/:id(.:format) admin/cars#destroy
98
+ cars_en GET /cars(.:format) cars#index {:locale=>"en"}
99
+ cars_es GET /es/coches(.:format) cars#index {:locale=>"es"}
100
+ cars_fr GET /fr/voitures(.:format) cars#index {:locale=>"fr"}
101
+ POST /cars(.:format) cars#create {:locale=>"en"}
102
+ POST /es/coches(.:format) cars#create {:locale=>"es"}
103
+ POST /fr/voitures(.:format) cars#create {:locale=>"fr"}
104
+ new_car_en GET /cars/new(.:format) cars#new {:locale=>"en"}
105
+ new_car_es GET /es/coches/nuevo(.:format) cars#new {:locale=>"es"}
106
+ new_car_fr GET /fr/voitures/nouveau(.:format) cars#new {:locale=>"fr"}
107
+ edit_car_en GET /cars/:id/edit(.:format) cars#edit {:locale=>"en"}
108
+ edit_car_es GET /es/coches/:id/edit(.:format) cars#edit {:locale=>"es"}
109
+ edit_car_fr GET /fr/voitures/:id/edit(.:format) cars#edit {:locale=>"fr"}
110
+ car_en GET /cars/:id(.:format) cars#show {:locale=>"en"}
111
+ car_es GET /es/coches/:id(.:format) cars#show {:locale=>"es"}
112
+ car_fr GET /fr/voitures/:id(.:format) cars#show {:locale=>"fr"}
113
+ PUT /cars/:id(.:format) cars#update {:locale=>"en"}
114
+ PUT /es/coches/:id(.:format) cars#update {:locale=>"es"}
115
+ PUT /fr/voitures/:id(.:format) cars#update {:locale=>"fr"}
116
+ DELETE /cars/:id(.:format) cars#destroy {:locale=>"en"}
117
+ DELETE /es/coches/:id(.:format) cars#destroy {:locale=>"es"}
118
+ DELETE /fr/voitures/:id(.:format) cars#destroy {:locale=>"fr"}
119
+ ```
120
+
121
+ Note that the route inside a `localized` are translated
122
+
123
+ 5. Include this filter in your `ApplicationController` if you want to set
124
+ the I18n.locale value from the value set in the route
125
+
126
+ ```ruby
127
+ before_filter :set_locale_from_url
128
+ ```
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rdoc/task'
4
+
5
+ desc 'Default: run tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the translate_routes plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc 'Generate documentation for the translate_routes plugin.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'RouteTranslator'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "route_translator", :path => ".."
4
+
5
+ gem "rails", "~>3.0.1"
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "route_translator", :path => ".."
4
+
5
+ gem "rails", "~>3.1.0"
@@ -0,0 +1,57 @@
1
+ require 'active_support'
2
+ require 'action_controller'
3
+ require 'action_mailer'
4
+ require 'action_dispatch'
5
+ require 'route_translator/route_set'
6
+
7
+ module RouteTranslator
8
+
9
+ TRANSLATABLE_SEGMENT = /^([-_a-zA-Z0-9]+)(\()?/.freeze
10
+ LOCALE_PARAM_KEY = :locale
11
+ ROUTE_HELPER_CONTAINER = [
12
+ ActionController::Base,
13
+ ActionView::Base,
14
+ ActionMailer::Base,
15
+ ActionDispatch::Routing::UrlFor
16
+ ].freeze
17
+
18
+ def self.locale_suffix locale
19
+ locale.to_s.underscore
20
+ end
21
+
22
+ # Attributes
23
+
24
+ module Controller
25
+ def set_locale_from_url
26
+ I18n.locale = params[RouteTranslator::LOCALE_PARAM_KEY]
27
+ end
28
+ end
29
+
30
+ module Mapper
31
+ #yield the block and add all the routes created
32
+ #in it to the localized_routes array
33
+ def localized
34
+ routes_before = @set.routes.map(&:to_s)
35
+ yield
36
+ routes_after = @set.routes.map(&:to_s)
37
+ @set.localized_routes ||= []
38
+ @set.localized_routes.concat(routes_after - routes_before)
39
+ @set.localized_routes.uniq!
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ # Add locale_suffix to controllers, views and mailers
46
+ RouteTranslator::ROUTE_HELPER_CONTAINER.each do |klass|
47
+ klass.class_eval do
48
+ private
49
+ def locale_suffix locale
50
+ RouteTranslator.locale_suffix locale
51
+ end
52
+ end
53
+ end
54
+
55
+ ActionController::Base.send(:include, RouteTranslator::Controller)
56
+ ActionDispatch::Routing::Mapper.send(:include, RouteTranslator::Mapper)
57
+ ActionDispatch::Routing::RouteSet.send(:include, RouteTranslator::RouteSet)
@@ -0,0 +1,36 @@
1
+ %w(helpers translator dictionary_management).each do |f|
2
+ require File.expand_path(File.join(File.dirname(__FILE__), 'route_set', f))
3
+ end
4
+
5
+
6
+ module RouteTranslator
7
+ module RouteSet
8
+ include DictionaryManagement
9
+ include Translator
10
+
11
+ attr_accessor :dictionary, :localized_routes
12
+
13
+ #Use the i18n setting from the app
14
+ def translate_with_i18n(*wanted_locales)
15
+ init_i18n_dictionary(*wanted_locales)
16
+ translate
17
+ end
18
+
19
+ #Use yield the block passing and empty dictionary as a paramenter
20
+ def translate_with_dictionary(&block)
21
+ yield_dictionary &block
22
+ translate
23
+ end
24
+
25
+ #Use the translations from the specified file
26
+ def translate_from_file(file_path = nil)
27
+ file_path ||= defined?(Rails) && File.join(Rails.root, %w(config i18-routes.yml))
28
+ load_dictionary_from_file(file_path)
29
+ translate
30
+ end
31
+
32
+ include Helpers
33
+ include Translator
34
+ include DictionaryManagement
35
+ end
36
+ end
@@ -0,0 +1,76 @@
1
+ module RouteTranslator
2
+ module RouteSet
3
+ module DictionaryManagement
4
+ # Resets dictionary and yields the block wich can be used to manually fill the dictionary
5
+ # with translations e.g.
6
+ # route_translator = RouteTranslator.new
7
+ # route_translator.yield_dictionary do |dict|
8
+ # dict['en'] = { 'people' => 'people' }
9
+ # dict['de'] = { 'people' => 'personen' }
10
+ # end
11
+ def yield_dictionary &block
12
+ reset_dictionary
13
+ yield @dictionary
14
+ set_available_locales_from_dictionary
15
+ end
16
+
17
+ # Resets dictionary and loads translations from specified file
18
+ # config/locales/routes.yml:
19
+ # en:
20
+ # people: people
21
+ # de:
22
+ # people: personen
23
+ # routes.rb:
24
+ # ... your routes ...
25
+ # ActionDispatch::Routing::Translator.translate_from_file
26
+ # or, to specify a custom file
27
+ # ActionDispatch::Routing::Translator.translate_from_file 'config', 'locales', 'routes.yml'
28
+ def load_dictionary_from_file file_path
29
+ reset_dictionary
30
+ add_dictionary_from_file file_path
31
+ end
32
+
33
+ # Add translations from another file to the dictionary.
34
+ def add_dictionary_from_file file_path
35
+ yaml = YAML.load_file(file_path)
36
+ yaml.each_pair do |locale, translations|
37
+ merge_translations locale, translations
38
+ end
39
+ set_available_locales_from_dictionary
40
+ end
41
+
42
+ # Merge translations for a specified locale into the dictionary
43
+ def merge_translations locale, translations
44
+ locale = locale.to_s
45
+ if translations.blank?
46
+ @dictionary[locale] ||= {}
47
+ return
48
+ end
49
+ @dictionary[locale] = (@dictionary[locale] || {}).merge(translations)
50
+ end
51
+
52
+ # Init dictionary to use I18n to translate route parts. Creates
53
+ # a hash with a block for each locale to lookup keys in I18n dynamically.
54
+ def init_i18n_dictionary *wanted_locales
55
+ wanted_locales = available_locales if wanted_locales.blank?
56
+ reset_dictionary
57
+ wanted_locales.each do |locale|
58
+ @dictionary[locale] = Hash.new do |hsh, key|
59
+ hsh[key] = I18n.translate key, :locale => locale #DISCUSS: caching or no caching (store key and translation in dictionary?)
60
+ end
61
+ end
62
+ @available_locales = @dictionary.keys.map &:to_s
63
+ end
64
+
65
+ private
66
+ def set_available_locales_from_dictionary
67
+ @available_locales = @dictionary.keys.map &:to_s
68
+ end
69
+
70
+ # Resets dictionary
71
+ def reset_dictionary
72
+ @dictionary = { default_locale => {}}
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,29 @@
1
+ module RouteTranslator
2
+ module RouteSet
3
+ module Helpers
4
+ def available_locales
5
+ @available_locales ||= I18n.available_locales.map(&:to_s)
6
+ end
7
+
8
+ def available_locales= locales
9
+ @available_locales = locales.map(&:to_s)
10
+ end
11
+
12
+ def default_locale
13
+ @default_locale ||= I18n.default_locale.to_s
14
+ end
15
+
16
+ def default_locale= locale
17
+ @default_locale = locale.to_s
18
+ end
19
+
20
+ def default_locale? locale
21
+ default_locale == locale.to_s
22
+ end
23
+
24
+ def locale_suffix locale
25
+ RouteTranslator.locale_suffix locale
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,140 @@
1
+ module RouteTranslator
2
+ module RouteSet
3
+ module Translator
4
+ # Translate a specific RouteSet, usually Rails.application.routes, but can
5
+ # be a RouteSet of a gem, plugin/engine etc.
6
+ def translate
7
+ Rails.logger.info "Translating routes (default locale: #{default_locale})" if defined?(Rails) && defined?(Rails.logger)
8
+
9
+ # save original routes and clear route set
10
+ original_routes = routes.dup
11
+ original_named_routes = named_routes.routes.dup # Hash {:name => :route}
12
+
13
+ routes_to_create = []
14
+ original_routes.each do |original_route|
15
+ if localized_routes && localized_routes.include?(original_route.to_s) then
16
+ translations_for(original_route).each do |translated_route_args|
17
+ routes_to_create << translated_route_args
18
+ end
19
+ else
20
+ route = untranslated_route original_route
21
+ routes_to_create << route
22
+ end
23
+ end
24
+
25
+ reset!
26
+
27
+ routes_to_create.each do |r|
28
+ add_route(*r)
29
+ end
30
+
31
+
32
+ Hash[original_named_routes.select{|k,v| localized_routes && localized_routes.include?(v.to_s)}].each_key do |route_name|
33
+ named_routes.helpers.concat add_untranslated_helpers_to_controllers_and_views(route_name)
34
+ end
35
+
36
+ finalize!
37
+ named_routes.install
38
+ end
39
+
40
+ # Add standard route helpers for default locale e.g.
41
+ # I18n.locale = :de
42
+ # people_path -> people_de_path
43
+ # I18n.locale = :fr
44
+ # people_path -> people_fr_path
45
+ def add_untranslated_helpers_to_controllers_and_views old_name
46
+ ['path', 'url'].map do |suffix|
47
+ new_helper_name = "#{old_name}_#{suffix}"
48
+
49
+ ROUTE_HELPER_CONTAINER.each do |helper_container|
50
+ helper_container.send :define_method, new_helper_name do |*args|
51
+ if respond_to? "#{old_name}_#{locale_suffix(I18n.locale)}_#{suffix}"
52
+ send "#{old_name}_#{locale_suffix(I18n.locale)}_#{suffix}", *args
53
+ else
54
+ send "#{old_name}_#{locale_suffix(I18n.default_locale)}_#{suffix}", *args
55
+ end
56
+ end
57
+ end
58
+
59
+ new_helper_name.to_sym
60
+ end
61
+ end
62
+
63
+ # Generate translations for a single route for all available locales
64
+ def translations_for route
65
+ available_locales.map do |locale|
66
+ translate_route(route, locale.dup) #we duplicate the locale string to ensure it's not frozen
67
+ end
68
+ end
69
+
70
+ # Generate translation for a single route for one locale
71
+ def translate_route route, locale
72
+ path_regex = route.path.respond_to?(:spec) ? route.path.spec : route.path
73
+
74
+ conditions = route.conditions.dup.merge({
75
+ :path_info => translate_path(path_regex.dup.to_s, locale)
76
+ })
77
+
78
+ conditions[:request_method] = request_method_array(conditions[:request_method]) if conditions[:request_method]
79
+
80
+ requirements = route.requirements.dup.merge!(LOCALE_PARAM_KEY => locale)
81
+ defaults = route.defaults.dup.merge LOCALE_PARAM_KEY => locale
82
+
83
+ new_name = "#{route.name}_#{locale_suffix(locale)}" if route.name
84
+
85
+ [route.app, conditions, requirements, defaults, new_name]
86
+ end
87
+
88
+ def untranslated_route route
89
+ path_regex = route.path.respond_to?(:spec) ? route.path.spec : route.path
90
+
91
+ conditions = route.conditions.dup.merge({
92
+ :path_info => path_regex.to_s
93
+ })
94
+
95
+ conditions[:request_method] = request_method_array(conditions[:request_method]) if conditions[:request_method]
96
+
97
+ [route.app, conditions, route.requirements.dup, route.defaults.dup, route.name]
98
+ end
99
+
100
+ def request_method_array(reg)
101
+ reg.source.gsub(%r{\^|\$}, "").split("|")
102
+ end
103
+
104
+ # Translates a path and adds the locale prefix.
105
+ def translate_path(path, locale)
106
+ final_optional_segments = path.slice!(/(\(.+\))$/)
107
+ new_path = path.split("/").map{|seg| translate_path_segment(seg, locale)}.join('/')
108
+ new_path = "/#{locale.downcase}#{new_path}" unless default_locale?(locale)
109
+ new_path = "/" if new_path.blank?
110
+ "#{new_path}#{final_optional_segments}"
111
+ end
112
+
113
+ # Tries to translate a single path segment. If the path segment
114
+ # contains sth. like a optional format "people(.:format)", only
115
+ # "people" will be translated, if there is no translation, the path
116
+ # segment is blank or begins with a ":" (param key), the segment
117
+ # is returned untouched
118
+ def translate_path_segment segment, locale
119
+ return segment if segment.blank? or segment.starts_with?(":")
120
+
121
+ match = TRANSLATABLE_SEGMENT.match(segment)[1] rescue nil
122
+
123
+ (translate_string(match, locale) || segment)
124
+ end
125
+
126
+ def translate_string(str, locale)
127
+ @dictionary[locale.to_s][str.to_s]
128
+ end
129
+
130
+ private
131
+
132
+ def reset!
133
+ clear!
134
+ named_routes.module.instance_methods.each do |method|
135
+ named_routes.module.send(:remove_method, method)
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,3 @@
1
+ module RouteTranslator
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,35 @@
1
+ namespace :translate_routes => :environment do
2
+
3
+ desc "Updates yaml translation files for the given languages"
4
+ task :update_yaml, :langs, :needs => :environment do |task, args|
5
+ segments = ActionController::Routing::Translator.original_static_segments
6
+
7
+ if args[:langs].is_a?(String)
8
+ langs = args[:langs] + ' ' + ActionController::Routing::Translator.default_locale
9
+ langs.split.uniq.each do |lang|
10
+
11
+ file_path = File.join(config_path, "routes_#{lang}.yml");
12
+
13
+ if File.exists?(file_path)
14
+ puts "Updating #{file_path}"
15
+ translations = YAML.load_file(file_path)
16
+ f = File.open(file_path,'w')
17
+ else
18
+ puts "Creating #{file_path}"
19
+ translations = {}
20
+ f = File.new(file_path, 'w')
21
+ end
22
+
23
+ f.write "#{lang}:\n"
24
+ segments.each do |s|
25
+ translation = translations[lang][s] rescue ''
26
+ f.write " #{s}: #{translation}\n"
27
+ end
28
+ f.close
29
+ end
30
+
31
+ else
32
+ puts 'Missing parameters, usage example: rake translate_routes:update_yaml["fr de es"]'
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+ require File.expand_path('../lib/route_translator/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "route_translator"
6
+ s.version = RouteTranslator::VERSION
7
+
8
+ s.authors = ["Raul Murciano", "Enric Lluelles"]
9
+ s.email = %q{enric@lluell.es}
10
+
11
+ s.homepage = %q{http://github.com/enriclluelles/route_translator}
12
+
13
+ s.description = %q{Translates the Rails routes of your application into the languages defined in your locale files}
14
+ s.summary = %q{Translate your Rails routes in a simple manner}
15
+
16
+ s.files = `git ls-files`.split($\) - %w(.gitignore test_runner)
17
+ s.test_files = `git ls-files test`.split($\)
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_runtime_dependency("mocha")
21
+ end
@@ -0,0 +1,10 @@
1
+ # es:
2
+ # people: gente
3
+ #
4
+ # en:
5
+
6
+ es:
7
+ people: gente
8
+ products: productos
9
+
10
+ en:
@@ -0,0 +1,328 @@
1
+ require 'test/unit'
2
+ require 'mocha'
3
+
4
+ require 'route_translator'
5
+
6
+ require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
7
+
8
+
9
+ class PeopleController < ActionController::Base; end
10
+ class ProductsController < ActionController::Base; end
11
+
12
+ class TranslateRoutesTest < ActionController::TestCase
13
+ include ActionDispatch::Assertions::RoutingAssertions
14
+ include RouteTranslator::TestHelper
15
+
16
+ def config_default_locale_settings(locale)
17
+ I18n.default_locale = locale
18
+ end
19
+
20
+ def setup
21
+ @controller = ActionController::Base.new
22
+ @view = ActionView::Base.new
23
+ @routes = ActionDispatch::Routing::RouteSet.new
24
+ end
25
+
26
+ def test_unnamed_root_route
27
+ @routes.draw do
28
+ localized do
29
+ root :to => 'people#index'
30
+ end
31
+ end
32
+ config_default_locale_settings 'en'
33
+ @routes.translate_with_dictionary{|t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
34
+
35
+ assert_routing '/', :controller => 'people', :action => 'index', :locale => 'en'
36
+ assert_routing '/es', :controller => 'people', :action => 'index', :locale => 'es'
37
+ end
38
+
39
+ def test_unnamed_root_route_without_prefix
40
+ @routes.draw do
41
+ localized do
42
+ root :to => 'people#index'
43
+ end
44
+ end
45
+ config_default_locale_settings 'es'
46
+
47
+ @routes.translate_from_file(File.expand_path('locales/routes.yml', File.dirname(__FILE__)))
48
+
49
+ assert_routing '/', :controller => 'people', :action => 'index', :locale => 'es'
50
+ assert_routing '/en', :controller => 'people', :action => 'index', :locale => 'en'
51
+ assert_unrecognized_route '/es', :controller => 'people', :action => 'index', :locale => 'es'
52
+ end
53
+
54
+ def test_unnamed_untranslated_route
55
+ @routes.draw do
56
+ localized do
57
+ match 'foo', :to => 'people#index'
58
+ end
59
+ end
60
+ config_default_locale_settings 'en'
61
+ @routes.translate_with_dictionary{|t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
62
+
63
+ assert_routing '/es/foo', :controller => 'people', :action => 'index', :locale => 'es'
64
+ assert_routing '/foo', :controller => 'people', :action => 'index', :locale => 'en'
65
+ end
66
+
67
+ def test_unnamed_translated_route_on_default_locale
68
+ @routes.draw { match 'people', :to => 'people#index' }
69
+ @routes.draw do
70
+ localized do
71
+ match 'people', :to => 'people#index'
72
+ end
73
+ end
74
+
75
+ config_default_locale_settings 'es'
76
+
77
+ @routes.translate_with_dictionary{|t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
78
+
79
+ assert_routing '/en/people', :controller => 'people', :action => 'index', :locale => 'en'
80
+ assert_routing '/gente', :controller => 'people', :action => 'index', :locale => 'es'
81
+ end
82
+
83
+ def test_unnamed_translated_route_on_non_default_locale
84
+ @routes.draw do
85
+ localized do
86
+ match 'people', :to => 'people#index'
87
+ end
88
+ end
89
+ config_default_locale_settings 'en'
90
+ @routes.translate_with_dictionary{|t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
91
+
92
+ assert_routing '/es/gente', :controller => 'people', :action => 'index', :locale => 'es'
93
+ assert_routing '/people', :controller => 'people', :action => 'index', :locale => 'en'
94
+ end
95
+
96
+ def test_named_translated_route_with_prefix_must_have_locale_as_static_segment
97
+ @routes.draw do
98
+ localized do
99
+ match 'people', :to => 'people#index'
100
+ end
101
+ end
102
+ config_default_locale_settings 'en'
103
+ @routes.translate_with_dictionary{|t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
104
+
105
+ # we check the string representation of the route,
106
+ # if it stores locale as a dynamic segment it would be represented as: "/:locale/gente"
107
+ assert_equal "/es/gente(.:format)", path_string(named_route('people_es'))
108
+ end
109
+
110
+ def test_named_empty_route_without_prefix
111
+ @routes.draw do
112
+ localized do
113
+ root :to => 'people#index', :as => 'people'
114
+ end
115
+ end
116
+ config_default_locale_settings 'es'
117
+ @routes.translate_with_dictionary{|t| t['es'] = {}; t['en'] = {'people' => 'gente'}; }
118
+
119
+ assert_routing '/en', :controller => 'people', :action => 'index', :locale => 'en'
120
+ assert_routing '/', :controller => 'people', :action => 'index', :locale => 'es'
121
+ assert_routing '', :controller => 'people', :action => 'index', :locale => 'es'
122
+ end
123
+
124
+ def test_named_root_route_without_prefix
125
+ @routes.draw do
126
+ localized do
127
+ root :to => 'people#index'
128
+ end
129
+ end
130
+
131
+ config_default_locale_settings 'es'
132
+
133
+ @routes.translate_from_file(File.expand_path('locales/routes.yml', File.dirname(__FILE__)))
134
+
135
+ assert_routing '/', :controller => 'people', :action => 'index', :locale => 'es'
136
+ assert_routing '/en', :controller => 'people', :action => 'index', :locale => 'en'
137
+ assert_unrecognized_route '/es', :controller => 'people', :action => 'index', :locale => 'es'
138
+ end
139
+
140
+ def test_named_untranslated_route_without_prefix
141
+ @routes.draw do
142
+ localized do
143
+ match 'foo', :to => 'people#index', :as => 'people'
144
+ end
145
+ end
146
+ config_default_locale_settings 'es'
147
+ @routes.translate_with_dictionary{ |t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
148
+
149
+ assert_routing '/en/foo', :controller => 'people', :action => 'index', :locale => 'en'
150
+ assert_routing '/foo', :controller => 'people', :action => 'index', :locale => 'es'
151
+ assert_helpers_include :people_en, :people_es, :people
152
+ end
153
+
154
+ def test_named_translated_route_on_default_locale_without_prefix
155
+ @routes.draw do
156
+ localized do
157
+ match 'people', :to => 'people#index', :as => 'people'
158
+ end
159
+ end
160
+
161
+ config_default_locale_settings 'es'
162
+
163
+ @routes.translate_with_dictionary{|t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
164
+
165
+ assert_routing '/en/people', :controller => 'people', :action => 'index', :locale => 'en'
166
+ assert_routing 'gente', :controller => 'people', :action => 'index', :locale => 'es'
167
+ assert_helpers_include :people_en, :people_es, :people
168
+ end
169
+
170
+ def test_named_translated_route_on_non_default_locale_without_prefix
171
+ @routes.draw do
172
+ localized do
173
+ match 'people', :to => 'people#index', :as => 'people'
174
+ end
175
+ end
176
+
177
+ config_default_locale_settings 'en'
178
+
179
+ @routes.translate_with_dictionary{|t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
180
+
181
+ assert_routing '/people', :controller => 'people', :action => 'index', :locale => 'en'
182
+ assert_routing '/es/gente', :controller => 'people', :action => 'index', :locale => 'es'
183
+ assert_helpers_include :people_en, :people_es, :people
184
+ end
185
+
186
+ def test_formatted_root_route
187
+ @routes.draw do
188
+ localized do
189
+ root :to => 'people#index', :as => 'root'
190
+ end
191
+ end
192
+
193
+ @routes.translate_with_dictionary{ |t| t['en'] = {}; t['es'] = {'people' => 'gente'} }
194
+
195
+ if formatted_root_route?
196
+ assert_equal '/(.:format)', path_string(named_route('root_en'))
197
+ assert_equal '/es(.:format)', path_string(named_route('root_es'))
198
+ else
199
+ assert_equal '/', path_string(named_route('root_en'))
200
+ assert_equal '/es', path_string(named_route('root_es'))
201
+ end
202
+ end
203
+
204
+ def test_routes_locale_prefixes_are_never_downcased
205
+ @routes.draw do
206
+ localized do
207
+ match 'people', :to => 'people#index', :as => 'people'
208
+ end
209
+ end
210
+ config_default_locale_settings 'en'
211
+
212
+ @routes.translate_with_dictionary{ |t| t['en'] = {}; t['ES'] = {'people' => 'Gente'} }
213
+
214
+ assert_routing '/es/Gente', :controller => 'people', :action => 'index', :locale => 'ES'
215
+ end
216
+
217
+ def test_languages_load_from_file
218
+ @routes.draw do
219
+ localized do
220
+ match 'people', :to => 'people#index', :as => 'people'
221
+ end
222
+ end
223
+ config_default_locale_settings 'en'
224
+
225
+ @routes.translate_from_file(File.expand_path('locales/routes.yml', File.dirname(__FILE__)))
226
+
227
+ assert_routing '/people', :controller => 'people', :action => 'index', :locale => 'en'
228
+ assert_routing '/es/gente', :controller => 'people', :action => 'index', :locale => 'es'
229
+ assert_helpers_include :people_en, :people_es, :people
230
+ end
231
+
232
+ def test_languages_load_from_file_without_dictionary_for_default_locale
233
+ @routes.draw do
234
+ localized do
235
+ match 'people', :to => 'people#index', :as => 'people'
236
+ end
237
+ end
238
+ config_default_locale_settings 'fr'
239
+
240
+ @routes.translate_from_file(File.expand_path('locales/routes.yml', File.dirname(__FILE__)))
241
+
242
+ assert_routing '/people', :controller => 'people', :action => 'index', :locale => 'fr'
243
+ assert_routing '/en/people', :controller => 'people', :action => 'index', :locale => 'en'
244
+ assert_routing '/es/gente', :controller => 'people', :action => 'index', :locale => 'es'
245
+ assert_helpers_include :people_fr, :people_en, :people_es, :people
246
+ end
247
+
248
+ def test_i18n_based_translations_setting_locales
249
+ @routes.draw do
250
+ localized do
251
+ match 'people', :to => 'people#index', :as => 'people'
252
+ end
253
+ end
254
+ config_default_locale_settings 'en'
255
+
256
+ I18n.backend = StubbedI18nBackend
257
+
258
+ @routes.translate_with_i18n('es')
259
+
260
+ assert_routing '/es/gente', :controller => 'people', :action => 'index', :locale => 'es'
261
+ assert_routing '/people', :controller => 'people', :action => 'index', :locale => 'en'
262
+ assert_helpers_include :people_en, :people_es, :people
263
+ end
264
+
265
+ def test_i18n_based_translations_taking_i18n_available_locales
266
+ @routes.draw do
267
+ localized do
268
+ match 'people', :to => 'people#index', :as => 'people'
269
+ end
270
+ end
271
+ config_default_locale_settings 'en'
272
+ I18n.stubs(:available_locales).at_least_once.returns StubbedI18nBackend.available_locales
273
+ I18n.backend = StubbedI18nBackend
274
+
275
+ @routes.translate_with_i18n
276
+
277
+ assert_routing '/fr/people', :controller => 'people', :action => 'index', :locale => 'fr'
278
+ assert_routing '/es/gente', :controller => 'people', :action => 'index', :locale => 'es'
279
+ assert_routing '/people', :controller => 'people', :action => 'index', :locale => 'en'
280
+ assert_helpers_include :people_fr, :people_en, :people_es, :people
281
+ end
282
+
283
+ def test_2_localized_blocks
284
+ @routes.draw do
285
+ localized do
286
+ match 'people', :to => 'people#index', :as => 'people'
287
+ end
288
+ localized do
289
+ match 'products', :to => 'products#index', :as => 'products'
290
+ end
291
+ end
292
+ config_default_locale_settings 'en'
293
+
294
+ @routes.translate_from_file(File.expand_path('locales/routes.yml', File.dirname(__FILE__)))
295
+
296
+ assert_routing '/es/gente', :controller => 'people', :action => 'index', :locale => 'es'
297
+ assert_routing '/es/productos', :controller => 'products', :action => 'index', :locale => 'es'
298
+ end
299
+
300
+ def test_not_localizing_routes_outside_blocks
301
+ @routes.draw do
302
+ localized do
303
+ match 'people', :to => 'people#index', :as => 'people'
304
+ end
305
+ match 'products', :to => 'products#index', :as => 'products'
306
+ end
307
+ config_default_locale_settings 'en'
308
+
309
+ @routes.translate_from_file(File.expand_path('locales/routes.yml', File.dirname(__FILE__)))
310
+
311
+ assert_routing '/es/gente', :controller => 'people', :action => 'index', :locale => 'es'
312
+ assert_routing '/products', :controller => 'products', :action => 'index'
313
+ assert_unrecognized_route '/es/productos', :controller => 'products', :action => 'index', :locale => 'es'
314
+ end
315
+
316
+ def test_action_controller_gets_locale_setter
317
+ ActionController::Base.instance_methods.include?('set_locale_from_url')
318
+ end
319
+
320
+ def test_action_controller_gets_locale_suffix_helper
321
+ ActionController::Base.instance_methods.include?('locale_suffix')
322
+ end
323
+
324
+ def test_action_view_gets_locale_suffix_helper
325
+ ActionView::Base.instance_methods.include?('locale_suffix')
326
+ end
327
+
328
+ end
@@ -0,0 +1,49 @@
1
+ class StubbedI18nBackend
2
+ @@translations = {
3
+ 'es' => { 'people' => 'gente'},
4
+ 'fr' => {} # empty on purpose to test behaviour on incompleteness scenarios
5
+ }
6
+
7
+ def self.translate(locale, key, options)
8
+ @@translations[locale.to_s][key] || options[:default]
9
+ rescue
10
+ options[:default]
11
+ end
12
+
13
+ def self.available_locales
14
+ @@translations.keys
15
+ end
16
+
17
+
18
+ end
19
+
20
+ module RouteTranslator
21
+ module TestHelper
22
+ def path_string(route)
23
+ path = route.respond_to?(:path) ? route.path : route.to_s.split(' ')[1]
24
+ path.respond_to?(:spec) ? path.spec.to_s : path.to_s
25
+ end
26
+
27
+ def named_route(name)
28
+ @routes.routes.detect{ |r| r.name == name }
29
+ end
30
+
31
+ def formatted_root_route?
32
+ !(defined?(ActionPack) && ActionPack::VERSION::MAJOR == 3 && ActionPack::VERSION::MINOR > 0)
33
+ end
34
+
35
+ def assert_helpers_include(*helpers)
36
+ helpers.each do |helper|
37
+ ['url', 'path'].each do |suffix|
38
+ [@controller, @view].each { |obj| assert_respond_to obj, "#{helper}_#{suffix}".to_sym }
39
+ end
40
+ end
41
+ end
42
+
43
+ def assert_unrecognized_route(route_path, options)
44
+ assert_raise ActionController::RoutingError do
45
+ assert_routing route_path, options
46
+ end
47
+ end
48
+ end
49
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: route_translator
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Raul Murciano
9
+ - Enric Lluelles
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-07-10 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: mocha
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ description: Translates the Rails routes of your application into the languages defined
32
+ in your locale files
33
+ email: enric@lluell.es
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - .travis.yml
39
+ - Gemfile
40
+ - LICENSE
41
+ - README.md
42
+ - Rakefile
43
+ - gemfiles/Gemfile.rails-3.0.x
44
+ - gemfiles/Gemfile.rails-3.1.x
45
+ - lib/route_translator.rb
46
+ - lib/route_translator/route_set.rb
47
+ - lib/route_translator/route_set/dictionary_management.rb
48
+ - lib/route_translator/route_set/helpers.rb
49
+ - lib/route_translator/route_set/translator.rb
50
+ - lib/route_translator/version.rb
51
+ - lib/tasks/route_translator.rake
52
+ - route_translator.gemspec
53
+ - test/locales/routes.yml
54
+ - test/route_translator_test.rb
55
+ - test/test_helper.rb
56
+ homepage: http://github.com/enriclluelles/route_translator
57
+ licenses: []
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 1.8.21
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Translate your Rails routes in a simple manner
80
+ test_files:
81
+ - test/locales/routes.yml
82
+ - test/route_translator_test.rb
83
+ - test/test_helper.rb