translation_panel 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.rspec +1 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +12 -0
  4. data/Gemfile.lock +135 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +3 -0
  7. data/Rakefile +39 -0
  8. data/VERSION +1 -0
  9. data/app/assets/javascripts/translation_panel.js.coffee +88 -0
  10. data/app/assets/stylesheets/translation_panel.css.scss +53 -0
  11. data/lib/generators/translation_panel/install_generator.rb +13 -0
  12. data/lib/translation_panel/filter.rb +40 -0
  13. data/lib/translation_panel/redis_backend.rb +65 -0
  14. data/lib/translation_panel.rb +62 -0
  15. data/spec/app/.gitignore +2 -0
  16. data/spec/app/app/controllers/admin/translations_controller.rb +7 -0
  17. data/spec/app/app/controllers/application_controller.rb +10 -0
  18. data/spec/app/app/controllers/home_controller.rb +4 -0
  19. data/spec/app/app/views/home/index.html.erb +10 -0
  20. data/spec/app/config/application.rb +18 -0
  21. data/spec/app/config/boot.rb +5 -0
  22. data/spec/app/config/environment.rb +5 -0
  23. data/spec/app/config/environments/development.rb +11 -0
  24. data/spec/app/config/environments/production.rb +11 -0
  25. data/spec/app/config/environments/test.rb +12 -0
  26. data/spec/app/config/initializers/i18n.rb +8 -0
  27. data/spec/app/config/initializers/secret_token.rb +1 -0
  28. data/spec/app/config/initializers/session_store.rb +1 -0
  29. data/spec/app/config/redis/test.conf +34 -0
  30. data/spec/app/config/redis.yml +8 -0
  31. data/spec/app/config/routes.rb +7 -0
  32. data/spec/app/config.ru +4 -0
  33. data/spec/app/db/.gitkeep +0 -0
  34. data/spec/integration/navigation_spec.rb +9 -0
  35. data/spec/lib/redis_backend_spec.rb +64 -0
  36. data/spec/spec_helper.rb +6 -0
  37. data/spec/translation_panel_spec.rb +71 -0
  38. data/translation_panel.gemspec +94 -0
  39. metadata +170 -0
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.2@translation_panel --create
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", ">=3.1.0.rc4"
4
+ gem 'redis'
5
+ gem 'yajl-ruby'
6
+
7
+ group :development, :test do
8
+ gem "sqlite3"
9
+ gem "capybara", ">= 0.4.0"
10
+ gem "rspec-rails", ">= 2.0.0.beta"
11
+ gem "jeweler"
12
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,135 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ actionmailer (3.1.0.rc4)
5
+ actionpack (= 3.1.0.rc4)
6
+ mail (~> 2.3.0)
7
+ actionpack (3.1.0.rc4)
8
+ activemodel (= 3.1.0.rc4)
9
+ activesupport (= 3.1.0.rc4)
10
+ builder (~> 3.0.0)
11
+ erubis (~> 2.7.0)
12
+ i18n (~> 0.6)
13
+ rack (~> 1.3.0)
14
+ rack-cache (~> 1.0.1)
15
+ rack-mount (~> 0.8.1)
16
+ rack-test (~> 0.6.0)
17
+ sprockets (~> 2.0.0.beta.10)
18
+ tzinfo (~> 0.3.27)
19
+ activemodel (3.1.0.rc4)
20
+ activesupport (= 3.1.0.rc4)
21
+ bcrypt-ruby (~> 2.1.4)
22
+ builder (~> 3.0.0)
23
+ i18n (~> 0.6)
24
+ activerecord (3.1.0.rc4)
25
+ activemodel (= 3.1.0.rc4)
26
+ activesupport (= 3.1.0.rc4)
27
+ arel (~> 2.1.1)
28
+ tzinfo (~> 0.3.27)
29
+ activeresource (3.1.0.rc4)
30
+ activemodel (= 3.1.0.rc4)
31
+ activesupport (= 3.1.0.rc4)
32
+ activesupport (3.1.0.rc4)
33
+ multi_json (~> 1.0)
34
+ arel (2.1.3)
35
+ bcrypt-ruby (2.1.4)
36
+ builder (3.0.0)
37
+ capybara (1.0.0)
38
+ mime-types (>= 1.16)
39
+ nokogiri (>= 1.3.3)
40
+ rack (>= 1.0.0)
41
+ rack-test (>= 0.5.4)
42
+ selenium-webdriver (~> 0.2.0)
43
+ xpath (~> 0.1.4)
44
+ childprocess (0.1.9)
45
+ ffi (~> 1.0.6)
46
+ diff-lcs (1.1.2)
47
+ erubis (2.7.0)
48
+ ffi (1.0.9)
49
+ git (1.2.5)
50
+ hike (1.2.0)
51
+ i18n (0.6.0)
52
+ jeweler (1.6.4)
53
+ bundler (~> 1.0)
54
+ git (>= 1.2.5)
55
+ rake
56
+ json_pure (1.5.3)
57
+ mail (2.3.0)
58
+ i18n (>= 0.4.0)
59
+ mime-types (~> 1.16)
60
+ treetop (~> 1.4.8)
61
+ mime-types (1.16)
62
+ multi_json (1.0.3)
63
+ nokogiri (1.5.0)
64
+ polyglot (0.3.1)
65
+ rack (1.3.1)
66
+ rack-cache (1.0.2)
67
+ rack (>= 0.4)
68
+ rack-mount (0.8.1)
69
+ rack (>= 1.0.0)
70
+ rack-ssl (1.3.2)
71
+ rack
72
+ rack-test (0.6.0)
73
+ rack (>= 1.0)
74
+ rails (3.1.0.rc4)
75
+ actionmailer (= 3.1.0.rc4)
76
+ actionpack (= 3.1.0.rc4)
77
+ activerecord (= 3.1.0.rc4)
78
+ activeresource (= 3.1.0.rc4)
79
+ activesupport (= 3.1.0.rc4)
80
+ bundler (~> 1.0)
81
+ railties (= 3.1.0.rc4)
82
+ railties (3.1.0.rc4)
83
+ actionpack (= 3.1.0.rc4)
84
+ activesupport (= 3.1.0.rc4)
85
+ rack-ssl (~> 1.3.2)
86
+ rake (>= 0.8.7)
87
+ rdoc (~> 3.4)
88
+ thor (~> 0.14.6)
89
+ rake (0.9.2)
90
+ rdoc (3.8)
91
+ redis (2.2.1)
92
+ rspec (2.6.0)
93
+ rspec-core (~> 2.6.0)
94
+ rspec-expectations (~> 2.6.0)
95
+ rspec-mocks (~> 2.6.0)
96
+ rspec-core (2.6.4)
97
+ rspec-expectations (2.6.0)
98
+ diff-lcs (~> 1.1.2)
99
+ rspec-mocks (2.6.0)
100
+ rspec-rails (2.6.1)
101
+ actionpack (~> 3.0)
102
+ activesupport (~> 3.0)
103
+ railties (~> 3.0)
104
+ rspec (~> 2.6.0)
105
+ rubyzip (0.9.4)
106
+ selenium-webdriver (0.2.2)
107
+ childprocess (>= 0.1.9)
108
+ ffi (>= 1.0.7)
109
+ json_pure
110
+ rubyzip
111
+ sprockets (2.0.0.beta.10)
112
+ hike (~> 1.0)
113
+ rack (~> 1.0)
114
+ tilt (!= 1.3.0, ~> 1.1)
115
+ sqlite3 (1.3.3)
116
+ thor (0.14.6)
117
+ tilt (1.3.2)
118
+ treetop (1.4.9)
119
+ polyglot (>= 0.3.1)
120
+ tzinfo (0.3.29)
121
+ xpath (0.1.4)
122
+ nokogiri (~> 1.3)
123
+ yajl-ruby (0.8.2)
124
+
125
+ PLATFORMS
126
+ ruby
127
+
128
+ DEPENDENCIES
129
+ capybara (>= 0.4.0)
130
+ jeweler
131
+ rails (>= 3.1.0.rc4)
132
+ redis
133
+ rspec-rails (>= 2.0.0.beta)
134
+ sqlite3
135
+ yajl-ruby
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2011 YOURNAME
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.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = TranslationPanel
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rake'
10
+ require 'rdoc/task'
11
+
12
+ require 'rspec/core'
13
+ require 'rspec/core/rake_task'
14
+
15
+ RSpec::Core::RakeTask.new(:spec)
16
+
17
+ task :default => :spec
18
+
19
+ Rake::RDocTask.new(:rdoc) do |rdoc|
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = 'TranslationPanel'
22
+ rdoc.options << '--line-numbers' << '--inline-source'
23
+ rdoc.rdoc_files.include('README.rdoc')
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ end
26
+
27
+ require 'jeweler'
28
+ Jeweler::Tasks.new do |gem|
29
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
30
+ gem.name = "translation_panel"
31
+ gem.homepage = "http://github.com/Mik-die/translation_panel"
32
+ gem.license = "MIT"
33
+ gem.summary = %Q{I18n backend}
34
+ gem.description = %Q{I18n backend, based on redis, with frontend panel for translations}
35
+ gem.email = "MikDiet@gmail.com"
36
+ gem.authors = ["Mik-die"]
37
+ # dependencies defined in Gemfile
38
+ end
39
+ Jeweler::RubygemsDotOrgTasks.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,88 @@
1
+ $.translator = translator =
2
+ initialize: ->
3
+ return false unless translationPanel?
4
+ translator.loadData().render()
5
+ loadData: ->
6
+ translator.translatesData = translationPanel.translates || []
7
+ translator.locale = translationPanel.locale
8
+ translator.save_action = translationPanel.action
9
+ translator
10
+ render: ->
11
+ translator.container = $('<div id="translator"></div>').html("Open Translate Panel")
12
+ translator.container.appendTo($('body'))
13
+ translator.renderPanel().renderForm().setHandlers()
14
+ renderPanel: ->
15
+ translator.panel = $('<div id="translator_panel"><ul/></div>').appendTo($('body')).dialog
16
+ autoOpen: false
17
+ dialogClass: 'translator'
18
+ position: ['right',30]
19
+ translator.addTranslates translator.translatesData
20
+ translator
21
+ translates: []
22
+ addTranslates: (translatesData) ->
23
+ translator.addTranslate t for t in translatesData
24
+ addTranslate: (translateData) ->
25
+ if translator.translates[translateData.key]?
26
+ translator.translates[translateData.key].element.remove()
27
+ translator.translates[translateData.key] =
28
+ value: translateData.value
29
+ element: $('<li/>').append($('<a href="#" />').html(translateData.key))
30
+ .append($('<span/>')).appendTo translator.panel.find('ul')
31
+ change_translate: ->
32
+ if this.value? and this.value!=""
33
+ this.element.find('span').text(this.value)
34
+ else
35
+ this.element.find('span').html("<i>no translation</i>")
36
+ translator.translates[translateData.key].change_translate()
37
+ renderForm: ->
38
+ translator.form = $('<form />').attr
39
+ id: 'translator_form'
40
+ "accept-charset": "UTF-8"
41
+ action: translator.save_action
42
+ method: "get"
43
+ $('<span id="translator_key_span" />').appendTo(translator.form)
44
+ $('<input type="hidden" />').appendTo(translator.form).attr
45
+ id: 'translator_key'
46
+ name: 'key'
47
+ $('<input type="text" />').appendTo(translator.form).attr
48
+ id: 'translator_value'
49
+ name: 'value'
50
+ $('<input type="hidden" />').appendTo(translator.form).attr
51
+ name: 'locale'
52
+ value: translator.locale
53
+ $('<input type="submit" />').appendTo(translator.form).attr
54
+ name: 'submit'
55
+ value: 'Update'
56
+ translator.form.appendTo($('body')).dialog
57
+ autoOpen: false
58
+ dialogClass: 'translator'
59
+ position: ['center',30]
60
+ width: 450
61
+ translator
62
+ setHandlers: ->
63
+ translator.container.click ->
64
+ if translator.panel.dialog('isOpen')
65
+ translator.panel.dialog('close')
66
+ else
67
+ translator.panel.dialog('open')
68
+ translator.panel.find('li a').click ->
69
+ translator.form.dialog('open') unless translator.form.dialog('isOpen')
70
+ $('#translator_key_span').html $(this).html()
71
+ $('#translator_key').val $(this).html()
72
+ $('#translator_value').val translator.translates[$(this).html()].value
73
+ false
74
+ translator.form.submit ->
75
+ request = $.ajax
76
+ url: translator.form.attr('action')
77
+ data: translator.form.serializeArray()
78
+ success: (data, textStatus, jqXHR) ->
79
+ jqXHR.translate.change_translate()
80
+ error: (jqXHR, status) ->
81
+ jqXHR.translate.element.find('span').html('<i>Error!! ('+status+')</i>')
82
+ request.translate = translator.translates[$('#translator_key').val()]
83
+ request.translate.value = $('#translator_value').val()
84
+ request.translate.element.find('span').html('<i>saving..</i>')
85
+ translator.form.dialog('close')
86
+ false
87
+
88
+ $ -> $.translator.initialize()
@@ -0,0 +1,53 @@
1
+ #translator {
2
+ position: fixed;
3
+ top: 0;
4
+ right: 0;
5
+ height: 25px;
6
+ width: 300px;
7
+ background: #f6a828;
8
+ color: #ffffff;
9
+ text-align: center;
10
+ font-weight: bold;
11
+ padding-top: 4px;
12
+ cursor: pointer;
13
+ }
14
+
15
+ #translator_form {
16
+ padding: 20px;
17
+ input { display:block; width:95%; }
18
+ #translator_key_span { font-weight: bold; }
19
+ }
20
+
21
+ .translator {
22
+ ul {
23
+ list-style-type: none;
24
+ margin:0;
25
+ padding: 10px 5px;
26
+ max-height:600px;
27
+ overflow: auto;
28
+ li {
29
+ a { display:block; }
30
+ span { display:block; padding: 0 0 5px 10px; }
31
+ }
32
+ }
33
+
34
+ .ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
35
+ .ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
36
+ .ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
37
+ .ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
38
+ .ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
39
+ .ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
40
+ .ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
41
+ .ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
42
+ .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
43
+ .ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
44
+ .ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
45
+ .ui-draggable .ui-dialog-titlebar { cursor: move; }
46
+
47
+ .ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; }
48
+ .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; }
49
+ .ui-widget-content { border: 1px solid #dddddd; background: #eeeeee; color: #333333; }
50
+ .ui-widget-content a { color: #333333; }
51
+ .ui-widget-header { border: 1px solid #e78f08; background: #f6a828; color: #ffffff; font-weight: bold; }
52
+ .ui-widget-header a { color: #ffffff; }
53
+ }
@@ -0,0 +1,13 @@
1
+ module TranslationPanel
2
+ class InstallGenerator < Rails::Generators::Base
3
+ source_root File.expand_path("../../../../app/assets", __FILE__)
4
+ desc "copies TranslationPanel's javascript and stylesheet into vendor/assets"
5
+
6
+ def copy_asset_files
7
+ copy_file "javascripts/translation_panel.js.coffee",
8
+ "vendor/assets/javascripts/translation_panel.js.coffee"
9
+ copy_file "stylesheets/translation_panel.css.scss",
10
+ "vendor/assets/stylesheets/translation_panel.css.scss"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,40 @@
1
+ module TranslationPanel
2
+ class Filter
3
+ include ActionView::Helpers::TagHelper
4
+
5
+ def initialize(save_url, condition = true)
6
+ @save_url = save_url
7
+ @condition = condition
8
+ end
9
+
10
+ def before(controller)
11
+ TranslationPanel.show! if controller.send(@condition)
12
+ true
13
+ end
14
+
15
+ def after(controller)
16
+ if controller.send(@condition)
17
+ case controller.response.content_type
18
+ when "text/html"
19
+ return unless page = controller.response.body
20
+ header_part = tag :link, :href => "/assets/translation_panel.css",
21
+ :media => "screen", :rel => "stylesheet", :type => "text/css"
22
+ header_part+= content_tag :script, "", :src => "/assets/translation_panel.js",
23
+ :type => "text/javascript"
24
+ page.insert page.index("</head>"), header_part
25
+ body_part = content_tag :script,
26
+ "translationPanel = {translates: #{TranslationPanel.values.to_json}, action: '#{@save_url}', locale: '#{I18n.locale}'}",
27
+ {:type => "text/javascript"}, false
28
+ page+= body_part
29
+ controller.response.body = page
30
+ when "text/javascript"
31
+ return unless controller.response.body
32
+ body_part = ";$.translator.addTranslates(#{TranslationPanel.values.to_json});"
33
+ controller.response.body+= body_part
34
+ end
35
+ TranslationPanel.kill!
36
+ end
37
+ true
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,65 @@
1
+ module TranslationPanel
2
+ class RedisBackend < I18n::Backend::KeyValue
3
+ def initialize(store)
4
+ @subtrees = false
5
+ @store = store
6
+ end
7
+
8
+ protected
9
+ def lookup(locale, key, scope = [], options = {})
10
+ key = normalize_flat_keys(locale, key, scope, options[:separator])
11
+ count = options[:count]
12
+ if !count && value = @store["#{locale}.#{key}"]
13
+ TranslationPanel.push key if TranslationPanel.show?
14
+ ActiveSupport::JSON.decode(value) if value
15
+ else # look in namespace
16
+ store_count_keys(locale, key) if count
17
+ full_keys = @store.keys "#{locale}.#{key}.*"
18
+ if full_keys.empty?
19
+ TranslationPanel.push key if TranslationPanel.show?
20
+ nil
21
+ else
22
+ keys = full_keys.map{ |full_key| full_key.partition("#{locale}.")[2] }
23
+ TranslationPanel.push keys if TranslationPanel.show?
24
+ flatten_result = full_keys.inject({}) do |result, full_key|
25
+ value = @store[full_key]
26
+ value = ActiveSupport::JSON.decode(value) if value
27
+ result.merge full_key.partition("#{locale}.#{key}.")[2] => value
28
+ end
29
+ expand_keys(flatten_result).deep_symbolize_keys
30
+ end
31
+ end
32
+ end
33
+
34
+ def expand_keys(flatten_hash)
35
+ expanded_hash = {}
36
+ flatten_hash.each do |key, value|
37
+ key_parts = key.partition "."
38
+ if key_parts[2].empty?
39
+ expanded_hash.deep_merge! key => value
40
+ else
41
+ expanded_hash.deep_merge! key_parts[0] => expand_keys(key_parts[2] => value)
42
+ end
43
+ end
44
+ expanded_hash
45
+ end
46
+
47
+ def store_count_keys(locale, key)
48
+ if TranslationPanel.show?
49
+ I18n.t!('i18n.plural.keys', :locale => locale).each do |k|
50
+ unless translate_present?(locale, "#{key}.#{k}")
51
+ I18n.backend.store_translations locale, {"#{key}.#{k}" => ""}, :escape => false
52
+ end
53
+ end
54
+ end
55
+ rescue
56
+ nil
57
+ end
58
+
59
+ def translate_present?(locale, key)
60
+ I18n.t!(key, :locale => locale)
61
+ rescue
62
+ false
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,62 @@
1
+ require "translation_panel/filter"
2
+ require "translation_panel/redis_backend"
3
+
4
+ # Using with Redis:
5
+ # 1. Run redis-server
6
+ # 2. Set up redis connection settings and register RedisTranslator::Backend as
7
+ # i18n backend in initializers:
8
+ # require 'redis_translator'
9
+ # I18n.backend = RedisTranslator::Backend.new(Redis.new)
10
+ # # or in chain with another backend
11
+ # 3. Create action which receive translation pair and saves it. Don't forget to
12
+ # create route for GET-requests to this action
13
+ # class Admin::TranslationsController < ApplicationController
14
+ # def new
15
+ # I18n.backend.store_translations params[:locale], {params[:key] => params[:value]},
16
+ # :escape => false
17
+ # render :text => 'ok', :content_type => "text/plain"
18
+ # end
19
+ # end
20
+ # 4. Create around filter for actions, where needed to show TranslationPanel
21
+ # frontend panel. Initializator receives path for action and condition.
22
+ # around_filter TranslationPanel::Filter.new('/admin/translations/new', :show_panel?)
23
+ # 5. TranslationPanel's javascript and stylesheet can be customized by copying
24
+ # necessary files from +app/assets+ to app's +vendor/assets+. It can be done
25
+ # automatically by run:
26
+ # $ rails generate redis_translator:install
27
+
28
+ module TranslationPanel
29
+ class << self
30
+ def show!
31
+ Thread.current[:translations_list] = []
32
+ Thread.current[:collect_translation_data] = true
33
+ end
34
+
35
+ def kill!
36
+ Thread.current[:translations_list] = nil
37
+ Thread.current[:collect_translation_data] = nil
38
+ end
39
+
40
+ def show?
41
+ Thread.current[:collect_translation_data]
42
+ end
43
+
44
+ def push value
45
+ Thread.current[:translations_list] << value
46
+ end
47
+
48
+ def values
49
+ Thread.current[:collect_translation_data] = false
50
+ Thread.current[:translations_list].uniq.map do |key|
51
+ value = get_value(key)
52
+ {:key => key, :value => value} if value.is_a?(String)
53
+ end.compact
54
+ end
55
+
56
+ def get_value(key)
57
+ I18n.translate! key
58
+ rescue
59
+ ""
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,2 @@
1
+ db/*.rdb
2
+ log/*.log
@@ -0,0 +1,7 @@
1
+ class Admin::TranslationsController < ApplicationController
2
+ def new
3
+ params[:value] = nil if params[:value].empty?
4
+ I18n.backend.store_translations params[:locale], {params[:key] => params[:value]}, :escape => false
5
+ render :text => 'ok', :content_type => "text/plain"
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery
3
+ respond_to :html
4
+ around_filter TranslationPanel::Filter.new('/admin/translations/new', :show_translator?)
5
+
6
+ protected
7
+ def show_translator?
8
+ params[:translator].present?
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ class HomeController < ApplicationController
2
+ def index
3
+ end
4
+ end
@@ -0,0 +1,10 @@
1
+ <html>
2
+ <head>
3
+ <title>Test page</title>
4
+ </head>
5
+ <body>
6
+ <%= t(:some_i18n_key) %>
7
+ <%= t('long.chain.of.keys') %>
8
+ <%=raw t(:html_part) %>
9
+ </body>
10
+ </html>
@@ -0,0 +1,18 @@
1
+ require File.expand_path('../boot', __FILE__)
2
+
3
+ require "action_controller/railtie"
4
+ require "action_mailer/railtie"
5
+ require "active_resource/railtie"
6
+
7
+ Bundler.require(:default, Rails.env) if defined?(Bundler)
8
+
9
+ module App
10
+ class Application < Rails::Application
11
+ config.i18n.default_locale = :ru
12
+ config.encoding = "utf-8"
13
+ config.assets.enabled = true
14
+ config.generators do |g|
15
+ g.test_framework :rspec
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+
3
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
4
+
5
+ require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
@@ -0,0 +1,5 @@
1
+ # Load the rails application
2
+ require File.expand_path('../application', __FILE__)
3
+
4
+ # Initialize the rails application
5
+ App::Application.initialize!
@@ -0,0 +1,11 @@
1
+ App::Application.configure do
2
+ config.cache_classes = false
3
+ config.whiny_nils = true
4
+ config.consider_all_requests_local = true
5
+ config.action_controller.perform_caching = false
6
+ config.action_mailer.raise_delivery_errors = false
7
+ config.active_support.deprecation = :log
8
+ config.action_dispatch.best_standards_support = :builtin
9
+ config.assets.compress = false
10
+ config.action_mailer.default_url_options = { :host => 'localhost:3000' }
11
+ end
@@ -0,0 +1,11 @@
1
+ App::Application.configure do
2
+ config.cache_classes = true
3
+ config.consider_all_requests_local = false
4
+ config.action_controller.perform_caching = true
5
+ config.serve_static_assets = false
6
+ config.assets.compress = true
7
+ config.assets.js_compressor = :uglifier
8
+ config.action_dispatch.x_sendfile_header = "X-Sendfile"
9
+ config.i18n.fallbacks = true
10
+ config.active_support.deprecation = :notify
11
+ end
@@ -0,0 +1,12 @@
1
+ App::Application.configure do
2
+ config.cache_classes = true
3
+ config.serve_static_assets = true
4
+ config.static_cache_control = "public, max-age=3600"
5
+ config.whiny_nils = true
6
+ config.consider_all_requests_local = true
7
+ config.action_controller.perform_caching = false
8
+ config.action_dispatch.show_exceptions = false
9
+ config.action_controller.allow_forgery_protection = false
10
+ config.action_mailer.delivery_method = :test
11
+ config.active_support.deprecation = :stderr
12
+ end
@@ -0,0 +1,8 @@
1
+ require 'redis'
2
+ require 'translation_panel'
3
+
4
+ MultiJson.engine = :yajl
5
+
6
+ redis = Redis.new(YAML::load_file(File.join(Rails.root, '/config/redis.yml'))[Rails.env].symbolize_keys)
7
+
8
+ I18n.backend = TranslationPanel::RedisBackend.new(redis)
@@ -0,0 +1 @@
1
+ App::Application.config.secret_token = '11111111112222222222333333333344444444444455555555555555'
@@ -0,0 +1 @@
1
+ App::Application.config.session_store :cookie_store, :key => '_app_session'
@@ -0,0 +1,34 @@
1
+ daemonize yes
2
+ pidfile spec/app/db/redis.pid
3
+ port 6381
4
+ timeout 300
5
+ loglevel verbose
6
+ logfile stdout
7
+ databases 16
8
+
9
+ save 900 1
10
+ save 300 10
11
+ save 60 10000
12
+ rdbcompression yes
13
+ dbfilename app_test.rdb
14
+ dir spec/app/db/
15
+
16
+ slave-serve-stale-data yes
17
+
18
+ appendonly no
19
+ appendfsync everysec
20
+ no-appendfsync-on-rewrite no
21
+
22
+ vm-enabled no
23
+ vm-swap-file /tmp/redis.swap
24
+ vm-max-memory 0
25
+ vm-page-size 32
26
+ vm-pages 134217728
27
+ vm-max-threads 4
28
+
29
+ hash-max-zipmap-entries 512
30
+ hash-max-zipmap-value 64
31
+ list-max-ziplist-entries 512
32
+ list-max-ziplist-value 64
33
+ set-max-intset-entries 512
34
+ activerehashing yes
@@ -0,0 +1,8 @@
1
+ development:
2
+ port: 6381
3
+
4
+ production:
5
+ port: 6381
6
+
7
+ test:
8
+ port: 6381
@@ -0,0 +1,7 @@
1
+ App::Application.routes.draw do
2
+ namespace :admin do
3
+ resources :translations, :only => :new
4
+ end
5
+
6
+ root :to => "home#index"
7
+ end
@@ -0,0 +1,4 @@
1
+ # This file is used by Rack-based servers to start the application.
2
+
3
+ require ::File.expand_path('../config/environment', __FILE__)
4
+ run App::Application
File without changes
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Navigation" do
4
+ include Capybara::DSL
5
+
6
+ it "should be a valid app" do
7
+ ::Rails.application.should be_a(App::Application)
8
+ end
9
+ end
@@ -0,0 +1,64 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe TranslationPanel::RedisBackend do
5
+ before :all do
6
+ I18n.backend.store.flushdb
7
+ end
8
+
9
+ it "stores data, represented by pair key-value" do
10
+ I18n.backend.store_translations "ru", {"simple.key" => "Simple key"}, :escape => false
11
+ I18n.translate("simple.key").should == "Simple key"
12
+ end
13
+
14
+ it "stores data, represented by hash" do
15
+ I18n.backend.store_translations "ru", :hashed => {:value => "Hashed Value"}
16
+ I18n.translate("hashed.value").should == "Hashed Value"
17
+ end
18
+
19
+ it "stores data, when some pairs" do
20
+ I18n.backend.store_translations "ru", {"first.key" => "First", "second.key" => "Second"}, :escape => false
21
+ I18n.translate("first.key").should == "First"
22
+ I18n.translate("second.key").should == "Second"
23
+ end
24
+
25
+ it "stores data, when some translations in hash" do
26
+ I18n.backend.store_translations "ru", :one => {:two => "Two", :three => "Three"}
27
+ I18n.translate("one.two").should == "Two"
28
+ I18n.translate("one.three").should == "Three"
29
+ end
30
+
31
+ it "searches in scope" do
32
+ I18n.translate(:key, :scope => :simple).should == "Simple key"
33
+ I18n.translate(:value, :scope => :hashed).should == "Hashed Value"
34
+ end
35
+
36
+ it "searches some results in one scope" do
37
+ I18n.translate([:two, :three], :scope =>:one).should == ["Two","Three"]
38
+ end
39
+
40
+ it "searches in namespace" do
41
+ I18n.translate(:one).should include(:two => "Two", :three => "Three")
42
+ end
43
+
44
+ it "searches deep in namespace" do
45
+ I18n.backend.store_translations "ru", :one => {:four => {:five => "Five"}}
46
+ I18n.translate(:one).should include(:four => {:five => "Five"})
47
+ end
48
+
49
+ it "interpolates strings" do
50
+ I18n.backend.store_translations "ru", :hello => "Hello %{name}!"
51
+ I18n.translate(:hello, :name => 'Mik').should == "Hello Mik!"
52
+ end
53
+
54
+ it "pluralizes translations" do
55
+ I18n.backend.store_translations "ru", :bug => {:one => "1 bug", :other => "%{count} bugs"}
56
+ I18n.translate(:bug, :count => 1).should == "1 bug"
57
+ I18n.translate(:bug, :count => 2).should == "2 bugs"
58
+ end
59
+
60
+ it 'deletes existing translation with nil value' do
61
+ I18n.backend.store_translations "ru", "simple.key" => nil
62
+ I18n.backend.store["simple.key"].should be_nil
63
+ end
64
+ end
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+
3
+ ENV["RAILS_ENV"] ||= 'test'
4
+ require File.expand_path(File.join(File.dirname(__FILE__), 'app/config/environment'))
5
+ require 'rspec'
6
+ require 'rspec/rails'
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe TranslationPanel do
4
+ it "should be valid" do
5
+ TranslationPanel.should be_a(Module)
6
+ end
7
+ end
8
+
9
+ describe HomeController, :type => :controller do
10
+ render_views
11
+ describe "index" do
12
+ before :all do
13
+ I18n.backend.store.flushdb
14
+ end
15
+
16
+ describe "normal processing" do
17
+ it "doesn't call TranslationPanel in filters" do
18
+ TranslationPanel.should_not_receive(:show!)
19
+ TranslationPanel.should_not_receive(:kill!)
20
+ get :index
21
+ end
22
+ end
23
+
24
+ describe "processing with TranslationPanel" do
25
+ it "shows links to TranslationPanel assets" do
26
+ get :index, :translator => true
27
+ response.body.should have_selector("link", :href => "/assets/redis_translator.css")
28
+ response.body.should have_selector("script", :src => "/assets/redis_translator.js")
29
+ end
30
+ end
31
+
32
+ describe "both processings" do
33
+ before :all do
34
+ I18n.backend.store_translations "ru", :some_i18n_key => "Some Key!"
35
+ I18n.backend.store_translations "ru", :html_part => "<b>Some HTML</b>"
36
+ end
37
+
38
+ it "shows existing tanslates" do
39
+ get :index
40
+ response.body.should include("Some Key!")
41
+ response.body.should have_selector("b") do |bold|
42
+ bold.should include("Some HTML")
43
+ end
44
+ end
45
+
46
+ it "shows stubs for absent translates" do
47
+ get :index
48
+ response.body.should have_selector("span.translation_missing",
49
+ :title => "translation missing: ru.long.chain.of.keys")
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ describe Admin::TranslationsController, :type => :controller do
56
+ describe "new" do
57
+ before :all do
58
+ I18n.backend.store.flushdb
59
+ end
60
+
61
+ it "saves new translation" do
62
+ get :new, :locale => "ru", :key => "some.key", :value => "some_value"
63
+ I18n.t("some.key", :locale => "ru").should == "some_value"
64
+ end
65
+
66
+ it 'updates existing translation' do
67
+ get :new, :locale => "ru", :key => "some.key", :value => "other_value"
68
+ I18n.t("some.key", :locale => "ru").should == "other_value"
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,94 @@
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 = %q{translation_panel}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = [%q{Mik-die}]
12
+ s.date = %q{2011-07-19}
13
+ s.description = %q{I18n backend, based on redis, with frontend panel for translations}
14
+ s.email = %q{MikDiet@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".rspec",
20
+ ".rvmrc",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "MIT-LICENSE",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "app/assets/javascripts/translation_panel.js.coffee",
28
+ "app/assets/stylesheets/translation_panel.css.scss",
29
+ "lib/generators/translation_panel/install_generator.rb",
30
+ "lib/translation_panel.rb",
31
+ "lib/translation_panel/filter.rb",
32
+ "lib/translation_panel/redis_backend.rb",
33
+ "spec/app/.gitignore",
34
+ "spec/app/app/controllers/admin/translations_controller.rb",
35
+ "spec/app/app/controllers/application_controller.rb",
36
+ "spec/app/app/controllers/home_controller.rb",
37
+ "spec/app/app/views/home/index.html.erb",
38
+ "spec/app/config.ru",
39
+ "spec/app/config/application.rb",
40
+ "spec/app/config/boot.rb",
41
+ "spec/app/config/environment.rb",
42
+ "spec/app/config/environments/development.rb",
43
+ "spec/app/config/environments/production.rb",
44
+ "spec/app/config/environments/test.rb",
45
+ "spec/app/config/initializers/i18n.rb",
46
+ "spec/app/config/initializers/secret_token.rb",
47
+ "spec/app/config/initializers/session_store.rb",
48
+ "spec/app/config/redis.yml",
49
+ "spec/app/config/redis/test.conf",
50
+ "spec/app/config/routes.rb",
51
+ "spec/app/db/.gitkeep",
52
+ "spec/integration/navigation_spec.rb",
53
+ "spec/lib/redis_backend_spec.rb",
54
+ "spec/spec_helper.rb",
55
+ "spec/translation_panel_spec.rb",
56
+ "translation_panel.gemspec"
57
+ ]
58
+ s.homepage = %q{http://github.com/Mik-die/translation_panel}
59
+ s.licenses = [%q{MIT}]
60
+ s.require_paths = [%q{lib}]
61
+ s.rubygems_version = %q{1.8.5}
62
+ s.summary = %q{I18n backend}
63
+
64
+ if s.respond_to? :specification_version then
65
+ s.specification_version = 3
66
+
67
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
68
+ s.add_runtime_dependency(%q<rails>, [">= 3.1.0.rc4"])
69
+ s.add_runtime_dependency(%q<redis>, [">= 0"])
70
+ s.add_runtime_dependency(%q<yajl-ruby>, [">= 0"])
71
+ s.add_development_dependency(%q<sqlite3>, [">= 0"])
72
+ s.add_development_dependency(%q<capybara>, [">= 0.4.0"])
73
+ s.add_development_dependency(%q<rspec-rails>, [">= 2.0.0.beta"])
74
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
75
+ else
76
+ s.add_dependency(%q<rails>, [">= 3.1.0.rc4"])
77
+ s.add_dependency(%q<redis>, [">= 0"])
78
+ s.add_dependency(%q<yajl-ruby>, [">= 0"])
79
+ s.add_dependency(%q<sqlite3>, [">= 0"])
80
+ s.add_dependency(%q<capybara>, [">= 0.4.0"])
81
+ s.add_dependency(%q<rspec-rails>, [">= 2.0.0.beta"])
82
+ s.add_dependency(%q<jeweler>, [">= 0"])
83
+ end
84
+ else
85
+ s.add_dependency(%q<rails>, [">= 3.1.0.rc4"])
86
+ s.add_dependency(%q<redis>, [">= 0"])
87
+ s.add_dependency(%q<yajl-ruby>, [">= 0"])
88
+ s.add_dependency(%q<sqlite3>, [">= 0"])
89
+ s.add_dependency(%q<capybara>, [">= 0.4.0"])
90
+ s.add_dependency(%q<rspec-rails>, [">= 2.0.0.beta"])
91
+ s.add_dependency(%q<jeweler>, [">= 0"])
92
+ end
93
+ end
94
+
metadata ADDED
@@ -0,0 +1,170 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: translation_panel
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Mik-die
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-07-19 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rails
17
+ requirement: &id001 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 3.1.0.rc4
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: redis
28
+ requirement: &id002 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: yajl-ruby
39
+ requirement: &id003 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *id003
48
+ - !ruby/object:Gem::Dependency
49
+ name: sqlite3
50
+ requirement: &id004 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *id004
59
+ - !ruby/object:Gem::Dependency
60
+ name: capybara
61
+ requirement: &id005 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 0.4.0
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *id005
70
+ - !ruby/object:Gem::Dependency
71
+ name: rspec-rails
72
+ requirement: &id006 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 2.0.0.beta
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: *id006
81
+ - !ruby/object:Gem::Dependency
82
+ name: jeweler
83
+ requirement: &id007 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: "0"
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: *id007
92
+ description: I18n backend, based on redis, with frontend panel for translations
93
+ email: MikDiet@gmail.com
94
+ executables: []
95
+
96
+ extensions: []
97
+
98
+ extra_rdoc_files:
99
+ - README.rdoc
100
+ files:
101
+ - .rspec
102
+ - .rvmrc
103
+ - Gemfile
104
+ - Gemfile.lock
105
+ - MIT-LICENSE
106
+ - README.rdoc
107
+ - Rakefile
108
+ - VERSION
109
+ - app/assets/javascripts/translation_panel.js.coffee
110
+ - app/assets/stylesheets/translation_panel.css.scss
111
+ - lib/generators/translation_panel/install_generator.rb
112
+ - lib/translation_panel.rb
113
+ - lib/translation_panel/filter.rb
114
+ - lib/translation_panel/redis_backend.rb
115
+ - spec/app/.gitignore
116
+ - spec/app/app/controllers/admin/translations_controller.rb
117
+ - spec/app/app/controllers/application_controller.rb
118
+ - spec/app/app/controllers/home_controller.rb
119
+ - spec/app/app/views/home/index.html.erb
120
+ - spec/app/config.ru
121
+ - spec/app/config/application.rb
122
+ - spec/app/config/boot.rb
123
+ - spec/app/config/environment.rb
124
+ - spec/app/config/environments/development.rb
125
+ - spec/app/config/environments/production.rb
126
+ - spec/app/config/environments/test.rb
127
+ - spec/app/config/initializers/i18n.rb
128
+ - spec/app/config/initializers/secret_token.rb
129
+ - spec/app/config/initializers/session_store.rb
130
+ - spec/app/config/redis.yml
131
+ - spec/app/config/redis/test.conf
132
+ - spec/app/config/routes.rb
133
+ - spec/app/db/.gitkeep
134
+ - spec/integration/navigation_spec.rb
135
+ - spec/lib/redis_backend_spec.rb
136
+ - spec/spec_helper.rb
137
+ - spec/translation_panel_spec.rb
138
+ - translation_panel.gemspec
139
+ homepage: http://github.com/Mik-die/translation_panel
140
+ licenses:
141
+ - MIT
142
+ post_install_message:
143
+ rdoc_options: []
144
+
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ none: false
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ hash: -173789987
153
+ segments:
154
+ - 0
155
+ version: "0"
156
+ required_rubygems_version: !ruby/object:Gem::Requirement
157
+ none: false
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: "0"
162
+ requirements: []
163
+
164
+ rubyforge_project:
165
+ rubygems_version: 1.8.5
166
+ signing_key:
167
+ specification_version: 3
168
+ summary: I18n backend
169
+ test_files: []
170
+