i18n_dashboard 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.md +80 -0
- data/Rakefile +40 -0
- data/app/assets/javascripts/i18n_dashboard/application.js +17 -0
- data/app/assets/javascripts/i18n_dashboard/bootstrap.js.coffee +4 -0
- data/app/assets/javascripts/i18n_dashboard/translations.js.coffee +20 -0
- data/app/assets/stylesheets/i18n_dashboard/application.css +13 -0
- data/app/assets/stylesheets/i18n_dashboard/bootstrap_and_overrides.css +7 -0
- data/app/assets/stylesheets/i18n_dashboard/translations.css +4 -0
- data/app/controllers/i18n_dashboard/application_controller.rb +4 -0
- data/app/controllers/i18n_dashboard/translations_controller.rb +21 -0
- data/app/helpers/i18n_dashboard/application_helper.rb +18 -0
- data/app/helpers/i18n_dashboard/translations_helper.rb +4 -0
- data/app/models/i18n_dashboard/translation.rb +44 -0
- data/app/views/i18n_dashboard/translations/index.html.haml +51 -0
- data/app/views/layouts/i18n_dashboard/application.html.haml +32 -0
- data/config/routes.rb +6 -0
- data/lib/assets/javascripts/jquery.autosize.js +192 -0
- data/lib/i18n_dashboard.rb +7 -0
- data/lib/i18n_dashboard/engine.rb +51 -0
- data/lib/i18n_dashboard/version.rb +3 -0
- data/lib/tasks/i18n_dashboard_tasks.rake +4 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/images/favicon.ico +0 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +60 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +24 -0
- data/test/dummy/config/environment.rb +6 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +14335 -0
- data/test/dummy/log/test.log +420 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/tmp/cache/assets/C17/E40/sprockets%2F4308b5901b3134f6b1541f8a69276431 +0 -0
- data/test/dummy/tmp/cache/assets/C27/B00/sprockets%2F1b3e7717131812885279d16959cad225 +0 -0
- data/test/dummy/tmp/cache/assets/C29/8A0/sprockets%2Fa899e19256a0845205636938010c6fe5 +0 -0
- data/test/dummy/tmp/cache/assets/C57/880/sprockets%2F7610404e10c842a8cb69d7741678e619 +0 -0
- data/test/dummy/tmp/cache/assets/C7A/CA0/sprockets%2Fe62364e35231a761b76b13037f954c2f +0 -0
- data/test/dummy/tmp/cache/assets/C87/3F0/sprockets%2F1b04f197ed371938f027965191b01ff8 +0 -0
- data/test/dummy/tmp/cache/assets/C88/5F0/sprockets%2F69589b2e73a9d05247a6744a00b9d086 +0 -0
- data/test/dummy/tmp/cache/assets/C8C/9A0/sprockets%2F61288a069b85281ec267a04d9865cf26 +0 -0
- data/test/dummy/tmp/cache/assets/C98/FD0/sprockets%2F0b08d527a505c32173844d01cbc8353b +0 -0
- data/test/dummy/tmp/cache/assets/CA0/550/sprockets%2F36f109f19a5f50760020a2350aee6b76 +0 -0
- data/test/dummy/tmp/cache/assets/CA2/670/sprockets%2Fde3f90c262d351a812781a381174c0e4 +0 -0
- data/test/dummy/tmp/cache/assets/CA2/790/sprockets%2Fa91323b76385b2492b331611cf3a1ee8 +0 -0
- data/test/dummy/tmp/cache/assets/CA7/C90/sprockets%2F1e61531b0c665288f42cd0e490de4362 +0 -0
- data/test/dummy/tmp/cache/assets/CAB/B40/sprockets%2Fdf24253c225938243de61f4b8e45b350 +0 -0
- data/test/dummy/tmp/cache/assets/CB2/880/sprockets%2Fdf63ab7177a704396db76e501e950065 +0 -0
- data/test/dummy/tmp/cache/assets/CB3/530/sprockets%2F33561d49d5d2e8b94325b5c34f056f70 +0 -0
- data/test/dummy/tmp/cache/assets/CB3/CE0/sprockets%2F061634a9f7e57e6a32b2163094cdf675 +0 -0
- data/test/dummy/tmp/cache/assets/CB7/BF0/sprockets%2F88d248598b413e816ecc4a301d702c89 +0 -0
- data/test/dummy/tmp/cache/assets/CC4/5E0/sprockets%2Fb4fb33ba57d762df1313232c20172c16 +0 -0
- data/test/dummy/tmp/cache/assets/CCF/160/sprockets%2F2a21280366fbd1b4b3b27f8b5b391175 +0 -0
- data/test/dummy/tmp/cache/assets/CD6/C30/sprockets%2F68a84eca6a0b8a025698c5a4f1553113 +0 -0
- data/test/dummy/tmp/cache/assets/CD7/690/sprockets%2F324f37142a49a78eb59c69dc043221ba +0 -0
- data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/CDA/4A0/sprockets%2Fa4de2f6e912b73e5465154c91360d2b4 +0 -0
- data/test/dummy/tmp/cache/assets/CDB/530/sprockets%2F065cdf3144ff76d3672a96b1c4b04534 +0 -0
- data/test/dummy/tmp/cache/assets/CDB/FE0/sprockets%2F40ead92d2446ed8d412cc6185222791e +0 -0
- data/test/dummy/tmp/cache/assets/CDC/1E0/sprockets%2F794ffe755fdbc072f65081d71400b132 +0 -0
- data/test/dummy/tmp/cache/assets/CDE/CB0/sprockets%2Fd0d9c1b1b5416f7830ff460128f267b8 +0 -0
- data/test/dummy/tmp/cache/assets/CDF/E80/sprockets%2F13e20c9963f428d80d010b7fd6916a6e +0 -0
- data/test/dummy/tmp/cache/assets/CE2/070/sprockets%2Fb88b47b5b7145e2d4a09586357a0d48a +0 -0
- data/test/dummy/tmp/cache/assets/CE3/290/sprockets%2F8140ecc79e862a4e363353a1f505f7c7 +0 -0
- data/test/dummy/tmp/cache/assets/CE3/910/sprockets%2F0f69e891e76f603460bb2d4be73754a0 +0 -0
- data/test/dummy/tmp/cache/assets/D04/A50/sprockets%2F9a5702b9d21b6b356fd1245a532eac48 +0 -0
- data/test/dummy/tmp/cache/assets/D09/490/sprockets%2Fa45021c6df1675785caf177e3a33b6f0 +0 -0
- data/test/dummy/tmp/cache/assets/D0A/9D0/sprockets%2Fe026d4c914a79065f1bdb1443c52e9e4 +0 -0
- data/test/dummy/tmp/cache/assets/D0E/220/sprockets%2F74dd8ae13eb4fd2039d80f63379710a3 +0 -0
- data/test/dummy/tmp/cache/assets/D12/340/sprockets%2F9c4299f1befb5b336eee0021648720e7 +0 -0
- data/test/dummy/tmp/cache/assets/D14/DF0/sprockets%2Fdc7d2699f906aa73df210a647b906f32 +0 -0
- data/test/dummy/tmp/cache/assets/D17/4C0/sprockets%2F700827acd727b6fc8361f454c68a5cd7 +0 -0
- data/test/dummy/tmp/cache/assets/D19/4B0/sprockets%2F81b5e5d4982b62a61608083ffd8aa9f4 +0 -0
- data/test/dummy/tmp/cache/assets/D1A/960/sprockets%2F5f29e96153e76650daf023ef6eb60c85 +0 -0
- data/test/dummy/tmp/cache/assets/D20/A40/sprockets%2Fa8a7dde52a9693a9c76c4521b659717e +0 -0
- data/test/dummy/tmp/cache/assets/D23/130/sprockets%2Fd23bf67170bd556698cf57dc98d74a43 +0 -0
- data/test/dummy/tmp/cache/assets/D27/B40/sprockets%2Fdeed00121b4375a3b720cef0d19825b4 +0 -0
- data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/D34/650/sprockets%2F506eb0870b70db1817ad51fa7e826fb1 +0 -0
- data/test/dummy/tmp/cache/assets/D35/130/sprockets%2F02d1845ddb029d6e52168ecd80bb443d +0 -0
- data/test/dummy/tmp/cache/assets/D37/0E0/sprockets%2F7d091415f6d7c0e87302b6cd2fc0d3e3 +0 -0
- data/test/dummy/tmp/cache/assets/D38/640/sprockets%2F4a303ec194ba2adad59e674b62666c72 +0 -0
- data/test/dummy/tmp/cache/assets/D44/3B0/sprockets%2F7f7402dea766db136ffa0a8e915e3645 +0 -0
- data/test/dummy/tmp/cache/assets/D45/900/sprockets%2F4ea0c0d9f777c1c3e6ac2c949c526358 +0 -0
- data/test/dummy/tmp/cache/assets/D47/AE0/sprockets%2Fda6c6f8491d0926e5bd66c99e1a4c502 +0 -0
- data/test/dummy/tmp/cache/assets/D4C/8F0/sprockets%2F91c3748be4d5d83687d62df1c745b4cb +0 -0
- data/test/dummy/tmp/cache/assets/D4D/C80/sprockets%2Fbd91e31998a5390c39c8dfe9e0b2a268 +0 -0
- data/test/dummy/tmp/cache/assets/D4E/010/sprockets%2F503bf76de6b580b6084697a0bce9ff94 +0 -0
- data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/dummy/tmp/cache/assets/D52/510/sprockets%2Fafa6f0e91783285a62239f19abd9e88f +0 -0
- data/test/dummy/tmp/cache/assets/D52/660/sprockets%2Fbd777ea944a4e6eac534918e3486dc66 +0 -0
- data/test/dummy/tmp/cache/assets/D53/1C0/sprockets%2F5f2c389e5a57cf95066e26bf39c29d5a +0 -0
- data/test/dummy/tmp/cache/assets/D54/690/sprockets%2F2e04d9409cb8227f7f85ef58c59fa0e6 +0 -0
- data/test/dummy/tmp/cache/assets/D59/0F0/sprockets%2F4270ea9dbd84ff48e02ba315a0ab7230 +0 -0
- data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/D5E/D60/sprockets%2Fa16542dae11a0c28f64da023d98e7bc5 +0 -0
- data/test/dummy/tmp/cache/assets/D66/1D0/sprockets%2F126f2983156b26d20a9dece1fe2cca54 +0 -0
- data/test/dummy/tmp/cache/assets/D67/550/sprockets%2F325bfe833d0bef2dbc4d4116992c272d +0 -0
- data/test/dummy/tmp/cache/assets/D6B/A30/sprockets%2F4953faaf0994fdf44c268a2d3a2e34a1 +0 -0
- data/test/dummy/tmp/cache/assets/D6C/730/sprockets%2F8a4c656c6d8f2d130805564fbf0decb2 +0 -0
- data/test/dummy/tmp/cache/assets/D6D/3A0/sprockets%2F5072c9166eb56e59594fd1cdcaa0a81b +0 -0
- data/test/dummy/tmp/cache/assets/D6D/860/sprockets%2F13bcde6be1a8a4a0699a69a12c9f3944 +0 -0
- data/test/dummy/tmp/cache/assets/D70/B60/sprockets%2F0e4af1c70210cea20082a802eb4cba0e +0 -0
- data/test/dummy/tmp/cache/assets/D74/170/sprockets%2Fa202c9cbd617a6e5a8522ee738f8da59 +0 -0
- data/test/dummy/tmp/cache/assets/D81/650/sprockets%2Fbf62048d8e17fec958bfc40f4f9337f1 +0 -0
- data/test/dummy/tmp/cache/assets/D86/550/sprockets%2Fe37f7c1b22f998df87811f6e19df1e5a +0 -0
- data/test/dummy/tmp/cache/assets/D89/610/sprockets%2Fc413e3b81dc0aaca2de9b91e8146320e +0 -0
- data/test/dummy/tmp/cache/assets/D8E/930/sprockets%2F3eefd876c4c21f796f2875ca958dd63f +0 -0
- data/test/dummy/tmp/cache/assets/D8F/350/sprockets%2Fa2f17c30139a4611cf50cbbeed328e9d +0 -0
- data/test/dummy/tmp/cache/assets/D9B/3B0/sprockets%2Fe136169a133e4e357fe0fdca6b27bdd7 +0 -0
- data/test/dummy/tmp/cache/assets/DA0/C20/sprockets%2F29563dbecedcba0e44f34d6f135769c2 +0 -0
- data/test/dummy/tmp/cache/assets/DA3/160/sprockets%2Fd553d92fe1ffa3e59eaebc7c10600797 +0 -0
- data/test/dummy/tmp/cache/assets/DA7/A80/sprockets%2Fccfbd170e39dfb846de914e97036b64a +0 -0
- data/test/dummy/tmp/cache/assets/DAA/DA0/sprockets%2Ff94eca2ba5f45236fc224fef7f25c579 +0 -0
- data/test/dummy/tmp/cache/assets/DAE/2F0/sprockets%2F5bf09d27baa4bbd128e151a2d0fa60a3 +0 -0
- data/test/dummy/tmp/cache/assets/DAE/E10/sprockets%2F0cd4f3341cb9a98f2d9d53d4ffc96f90 +0 -0
- data/test/dummy/tmp/cache/assets/DB7/310/sprockets%2F1fbb9e98965bbf2ac579e2b31dd8f894 +0 -0
- data/test/dummy/tmp/cache/assets/DBD/790/sprockets%2Fe83ef87fc9a96e7df0ea82d5f7542e09 +0 -0
- data/test/dummy/tmp/cache/assets/DC4/310/sprockets%2Fce37cfc9e43c166c4f6e0a426602acca +0 -0
- data/test/dummy/tmp/cache/assets/DC8/410/sprockets%2Fbce5aec46efaa4562dd4397cd6c26113 +0 -0
- data/test/dummy/tmp/cache/assets/DD1/1D0/sprockets%2F3ba28ff187fc1853310effb2ffc77be2 +0 -0
- data/test/dummy/tmp/cache/assets/DD7/DC0/sprockets%2F94f1451efc5534b6e18becb9ff751aed +0 -0
- data/test/dummy/tmp/cache/assets/DDB/660/sprockets%2F4f4d7807fefc271ae9681d46cbfa7d3c +0 -0
- data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/DDC/780/sprockets%2F6cdaf95075508fdf1d6f9caed58bc912 +0 -0
- data/test/dummy/tmp/cache/assets/DDC/AF0/sprockets%2Fc930d4549ca9885a8fec4ebb7b64bec4 +0 -0
- data/test/dummy/tmp/cache/assets/DE3/1E0/sprockets%2F585396ba7ec073f6dfbfb617e9c6ae7e +0 -0
- data/test/dummy/tmp/cache/assets/DE6/600/sprockets%2F9cc3fd4df486f860e8d7ddf3645c0ef4 +0 -0
- data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/E10/1F0/sprockets%2F9fe678546e13bdced97f0143fdca7efc +0 -0
- data/test/dummy/tmp/cache/assets/E4D/ED0/sprockets%2Faf66ce6d0cda4622cc0ddeabf0950ca8 +0 -0
- data/test/dummy/tmp/cache/assets/E53/920/sprockets%2Fb53cddcfb4c8ce49c1e0610f4b0be9be +0 -0
- data/test/dummy/tmp/cache/assets/E87/E70/sprockets%2Fa441ed46c0f3abe7f8f6dc0b3ad8ecce +0 -0
- data/test/functional/i18n_dashboard/translations_controller_test.rb +9 -0
- data/test/i18n_dashboard_test.rb +7 -0
- data/test/test_helper.rb +18 -0
- data/test/unit/helpers/i18n_dashboard/translations_helper_test.rb +6 -0
- data/test/unit/i18n_dashboard/translation_test.rb +75 -0
- metadata +491 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 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.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# I18n Dashboard
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/fourmach/i18n_dashboard.png?branch=master)](https://travis-ci.org/fourmach/i18n_dashboard)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/i18n_dashboard.png)](http://badge.fury.io/rb/i18n_dashboard)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/fourmach/i18n_dashboard.png)](https://codeclimate.com/github/fourmach/i18n_dashboard)
|
6
|
+
[![Coverage Status](https://coveralls.io/repos/fourmach/i18n_dashboard/badge.png?branch=master)](https://coveralls.io/r/fourmach/i18n_dashboard?branch=master)
|
7
|
+
|
8
|
+
|
9
|
+
Rails Engine for I18n management with redis as backend.
|
10
|
+
|
11
|
+
## Screenshots
|
12
|
+
|
13
|
+
![#](https://raw.github.com/fourmach/i18n_dashboard/master/doc/screenshot-1.png)
|
14
|
+
![#](https://raw.github.com/fourmach/i18n_dashboard/master/doc/screenshot-2.png)
|
15
|
+
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
``` ruby
|
22
|
+
gem 'i18n_dashboard'
|
23
|
+
```
|
24
|
+
|
25
|
+
|
26
|
+
And then execute:
|
27
|
+
|
28
|
+
$ bundle
|
29
|
+
|
30
|
+
Modify your config/routes.rb and add:
|
31
|
+
|
32
|
+
``` ruby
|
33
|
+
mount I18nDashboard::Engine => '/i18n_dashboard'
|
34
|
+
```
|
35
|
+
|
36
|
+
Add to the bottom of config/environment.rb
|
37
|
+
``` ruby
|
38
|
+
I18nDashboard::Engine.load!
|
39
|
+
```
|
40
|
+
|
41
|
+
|
42
|
+
## Customize
|
43
|
+
|
44
|
+
Redis configuration:
|
45
|
+
|
46
|
+
``` ruby
|
47
|
+
I18nDashboard::Engine.redis = Redis.new
|
48
|
+
```
|
49
|
+
|
50
|
+
Customize layout
|
51
|
+
|
52
|
+
``` ruby
|
53
|
+
I18nDashboard::TranslationsController.layout 'admin'
|
54
|
+
```
|
55
|
+
And add `//= require i18n_dashboard/application` to your javascript.
|
56
|
+
|
57
|
+
|
58
|
+
But beware, you may have to change somethings in your template like:
|
59
|
+
|
60
|
+
``` ruby
|
61
|
+
# partials
|
62
|
+
render 'admin_menu'
|
63
|
+
|
64
|
+
# change by for example
|
65
|
+
render 'application/admin_menu'
|
66
|
+
|
67
|
+
# or routes
|
68
|
+
admin_root_path
|
69
|
+
# add the prefix main_app
|
70
|
+
main_app.admin_root_path
|
71
|
+
```
|
72
|
+
|
73
|
+
|
74
|
+
## Contributing
|
75
|
+
|
76
|
+
1. Fork it
|
77
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
78
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
79
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
80
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'I18nDashboard'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
24
|
+
load 'rails/tasks/engine.rake'
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
Bundler::GemHelper.install_tasks
|
29
|
+
|
30
|
+
require 'rake/testtask'
|
31
|
+
|
32
|
+
Rake::TestTask.new(:test) do |t|
|
33
|
+
t.libs << 'lib'
|
34
|
+
t.libs << 'test'
|
35
|
+
t.pattern = 'test/**/*_test.rb'
|
36
|
+
t.verbose = false
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
task :default => :test
|
@@ -0,0 +1,17 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// the compiled file.
|
9
|
+
//
|
10
|
+
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
11
|
+
// GO AFTER THE REQUIRES BELOW.
|
12
|
+
//
|
13
|
+
//= require jquery
|
14
|
+
//= require jquery_ujs
|
15
|
+
//= require jquery.autosize
|
16
|
+
//= require twitter/bootstrap
|
17
|
+
//= require_tree .
|
@@ -0,0 +1,20 @@
|
|
1
|
+
jQuery ->
|
2
|
+
$('#form-translation textarea').autosize()
|
3
|
+
|
4
|
+
$('.add-translation').click (event) ->
|
5
|
+
event.preventDefault()
|
6
|
+
$('#form-translation #locale').val('')
|
7
|
+
$('#form-translation #key').val('')
|
8
|
+
$('#form-translation #value').val('')
|
9
|
+
$('#form-translation #value').trigger('autosize')
|
10
|
+
$('#form-translation #value').focus()
|
11
|
+
|
12
|
+
$('.edit-translation').click (event) ->
|
13
|
+
event.preventDefault()
|
14
|
+
$('#form-translation #locale').val($(@).data('locale'))
|
15
|
+
$('#form-translation #key').val($(@).data('key'))
|
16
|
+
$('#form-translation #value').val($(@).html().trim())
|
17
|
+
|
18
|
+
$('#form-translation #value').trigger('autosize')
|
19
|
+
$('#form-translation #value').focus()
|
20
|
+
$('#form-translation #value')[0].setSelectionRange(0,0)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
9
|
+
* compiled file, but it's generally better to create a new file per style scope.
|
10
|
+
*
|
11
|
+
*= require_self
|
12
|
+
*= require_tree .
|
13
|
+
*/
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_dependency "i18n_dashboard/application_controller"
|
2
|
+
|
3
|
+
module I18nDashboard
|
4
|
+
class TranslationsController < ApplicationController
|
5
|
+
|
6
|
+
def index
|
7
|
+
@translations = Translation.all(params[:query])
|
8
|
+
end
|
9
|
+
|
10
|
+
def create
|
11
|
+
Translation.create(params[:key], params[:value], params[:locale])
|
12
|
+
redirect_to root_path, :notice => "Added translations"
|
13
|
+
end
|
14
|
+
|
15
|
+
def destroy
|
16
|
+
Translation.destroy(params[:id])
|
17
|
+
redirect_to root_path, :notice => "Key deleted"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module I18nDashboard
|
2
|
+
module ApplicationHelper
|
3
|
+
def title(page_title, show_title = true)
|
4
|
+
@show_title = show_title
|
5
|
+
if @show_title
|
6
|
+
content_for(:title) { h(page_title.to_s) }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def show_title?
|
11
|
+
@show_title
|
12
|
+
end
|
13
|
+
|
14
|
+
def humanize_key(key)
|
15
|
+
key.split('.').last.gsub('_', ' ').gsub(/\b('?[a-z])/) { $1.capitalize }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module I18nDashboard
|
2
|
+
class Translation
|
3
|
+
|
4
|
+
|
5
|
+
def self.all(string = nil)
|
6
|
+
keys = Engine.redis.sunion('keys', 'system_keys')
|
7
|
+
if string.present?
|
8
|
+
keys = keys.grep(/#{string}/)
|
9
|
+
end
|
10
|
+
keys.sort
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.keys
|
14
|
+
Engine.redis.smembers('keys').sort!
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.system_keys
|
18
|
+
Engine.redis.smembers('system_keys').sort!
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.system_key?(key)
|
22
|
+
Engine.redis.sismember('system_keys', key)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.create(key, value, locale)
|
26
|
+
if key.present?
|
27
|
+
Engine.redis.sadd('keys', key)
|
28
|
+
I18n.backend.store_translations(locale, {key => value}, :escape => false)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.exists?(key, locale)
|
33
|
+
Engine.redis.exists("#{locale}.#{key}")
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.destroy(key)
|
37
|
+
Engine.redis.srem('keys', key)
|
38
|
+
I18n.available_locales.each do |locale|
|
39
|
+
Engine.redis.del("#{locale}.#{key}")
|
40
|
+
end
|
41
|
+
I18n.reload!
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
- title "Translations"
|
2
|
+
|
3
|
+
#add-translation.modal.hide.fade
|
4
|
+
.modal-header
|
5
|
+
%a.close{"data-dismiss" => "modal"} ×
|
6
|
+
%h3 Translation
|
7
|
+
.modal-body
|
8
|
+
.row
|
9
|
+
= form_tag translations_path, id: 'form-translation' do
|
10
|
+
.span1
|
11
|
+
= label_tag :locale
|
12
|
+
= text_field_tag :locale, '', class: 'span1', autofocus: true
|
13
|
+
|
14
|
+
.span4
|
15
|
+
= label_tag :key
|
16
|
+
= text_field_tag :key, '', class: 'span4'
|
17
|
+
|
18
|
+
.span5
|
19
|
+
= label_tag :value
|
20
|
+
= text_area_tag :value, '', class: 'span5'
|
21
|
+
.span2
|
22
|
+
%br
|
23
|
+
= button_tag "Submit", class: 'btn'
|
24
|
+
|
25
|
+
.navbar
|
26
|
+
.navbar-inner
|
27
|
+
= form_tag translations_path, class: 'navbar-form pull-left', method: :get do
|
28
|
+
= text_field_tag :query, params[:query], hint: 'Search', autofocus: true
|
29
|
+
= button_tag 'search', class: 'btn'
|
30
|
+
= link_to 'clear', translations_path, class: 'btn'
|
31
|
+
|
32
|
+
.pull-right
|
33
|
+
= link_to 'new', '#', data: { toggle: "modal", target: '#add-translation'}, class: 'btn add-translation'
|
34
|
+
%hr/
|
35
|
+
%table.table.table-hover
|
36
|
+
%thead
|
37
|
+
%th key
|
38
|
+
- I18n.available_locales.each do |locale|
|
39
|
+
%th= locale
|
40
|
+
%tbody
|
41
|
+
- @translations.each do |key|
|
42
|
+
%tr
|
43
|
+
%td= key
|
44
|
+
- I18n.available_locales.each do |locale|
|
45
|
+
%td
|
46
|
+
= link_to '#', data: {key: key, locale: locale, toggle: "modal", target: '#add-translation'}, class: "edit-translation #{(I18nDashboard::Translation.exists?(key, locale) ? nil : 'text-error')}" do
|
47
|
+
= t(key, locale: locale, default: humanize_key(key))
|
48
|
+
%td
|
49
|
+
- unless I18nDashboard::Translation.system_key?(key)
|
50
|
+
= link_to translation_path(key), method: :delete, class: 'btn btn-mini btn-danger' do
|
51
|
+
%i.icon-trash
|
@@ -0,0 +1,32 @@
|
|
1
|
+
!!!
|
2
|
+
%html{lang: I18n.locale}
|
3
|
+
%head
|
4
|
+
%meta{:charset => "utf-8"}
|
5
|
+
%title
|
6
|
+
= content_for?(:title) ? (yield(:title) + " - I18n Dashboard") : "I18n Dashboard"
|
7
|
+
%meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"}
|
8
|
+
%meta{:content => "Fourmach.com", :name => "author"}
|
9
|
+
|
10
|
+
= stylesheet_link_tag "i18n_dashboard/application", media: nil
|
11
|
+
|
12
|
+
/ Le HTML5 shim, for IE6-8 support of HTML5 elements
|
13
|
+
/[if lt IE 9]
|
14
|
+
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
15
|
+
|
16
|
+
= csrf_meta_tag
|
17
|
+
%body{id: "#{controller.controller_name}-#{controller.action_name}"}
|
18
|
+
/ = render 'application/admin_menu'
|
19
|
+
|
20
|
+
.container
|
21
|
+
%h1= yield(:title)
|
22
|
+
|
23
|
+
#flash
|
24
|
+
- flash.each do |key, value|
|
25
|
+
.alert{ :class => "alert-#{key}" }
|
26
|
+
= value
|
27
|
+
|
28
|
+
|
29
|
+
= yield
|
30
|
+
|
31
|
+
= javascript_include_tag "i18n_dashboard/application"
|
32
|
+
|
data/config/routes.rb
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
/*!
|
2
|
+
jQuery Autosize v1.16.7
|
3
|
+
(c) 2013 Jack Moore - jacklmoore.com
|
4
|
+
updated: 2013-03-20
|
5
|
+
license: http://www.opensource.org/licenses/mit-license.php
|
6
|
+
*/
|
7
|
+
|
8
|
+
|
9
|
+
(function ($) {
|
10
|
+
var
|
11
|
+
defaults = {
|
12
|
+
className: 'autosizejs',
|
13
|
+
append: '',
|
14
|
+
callback: false
|
15
|
+
},
|
16
|
+
hidden = 'hidden',
|
17
|
+
borderBox = 'border-box',
|
18
|
+
lineHeight = 'lineHeight',
|
19
|
+
supportsScrollHeight,
|
20
|
+
|
21
|
+
// border:0 is unnecessary, but avoids a bug in FireFox on OSX (http://www.jacklmoore.com/autosize#comment-851)
|
22
|
+
copy = '<textarea tabindex="-1" style="position:absolute; top:-999px; left:0; right:auto; bottom:auto; border:0; -moz-box-sizing:content-box; -webkit-box-sizing:content-box; box-sizing:content-box; word-wrap:break-word; height:0 !important; min-height:0 !important; width: 0; overflow:hidden;"/>',
|
23
|
+
|
24
|
+
// line-height is conditionally included because IE7/IE8/old Opera do not return the correct value.
|
25
|
+
copyStyle = [
|
26
|
+
'fontFamily',
|
27
|
+
'fontSize',
|
28
|
+
'fontWeight',
|
29
|
+
'fontStyle',
|
30
|
+
'letterSpacing',
|
31
|
+
'textTransform',
|
32
|
+
'wordSpacing',
|
33
|
+
'textIndent'
|
34
|
+
],
|
35
|
+
oninput = 'oninput',
|
36
|
+
onpropertychange = 'onpropertychange',
|
37
|
+
|
38
|
+
// to keep track which textarea is being mirrored when adjust() is called.
|
39
|
+
mirrored,
|
40
|
+
|
41
|
+
// the mirror element, which is used to calculate what size the mirrored element should be.
|
42
|
+
mirror = $(copy).data('autosize', true)[0];
|
43
|
+
|
44
|
+
// test that line-height can be accurately copied.
|
45
|
+
mirror.style.lineHeight = '99px';
|
46
|
+
if ($(mirror).css(lineHeight) === '99px') {
|
47
|
+
copyStyle.push(lineHeight);
|
48
|
+
}
|
49
|
+
mirror.style.lineHeight = '';
|
50
|
+
|
51
|
+
$.fn.autosize = function (options) {
|
52
|
+
options = $.extend({}, defaults, options || {});
|
53
|
+
|
54
|
+
if (mirror.parentNode !== document.body) {
|
55
|
+
$(document.body).append(mirror);
|
56
|
+
|
57
|
+
mirror.value = "\n\n\n";
|
58
|
+
mirror.scrollTop = 9e4;
|
59
|
+
supportsScrollHeight = mirror.scrollHeight === mirror.scrollTop + mirror.clientHeight;
|
60
|
+
}
|
61
|
+
|
62
|
+
return this.each(function () {
|
63
|
+
var
|
64
|
+
ta = this,
|
65
|
+
$ta = $(ta),
|
66
|
+
minHeight,
|
67
|
+
active,
|
68
|
+
resize,
|
69
|
+
boxOffset = 0,
|
70
|
+
callback = $.isFunction(options.callback);
|
71
|
+
|
72
|
+
if ($ta.data('autosize')) {
|
73
|
+
// exit if autosize has already been applied, or if the textarea is the mirror element.
|
74
|
+
return;
|
75
|
+
}
|
76
|
+
|
77
|
+
if ($ta.css('box-sizing') === borderBox || $ta.css('-moz-box-sizing') === borderBox || $ta.css('-webkit-box-sizing') === borderBox){
|
78
|
+
boxOffset = $ta.outerHeight() - $ta.height();
|
79
|
+
}
|
80
|
+
|
81
|
+
minHeight = Math.max(parseInt($ta.css('minHeight'), 10) - boxOffset, $ta.height());
|
82
|
+
|
83
|
+
resize = ($ta.css('resize') === 'none' || $ta.css('resize') === 'vertical') ? 'none' : 'horizontal';
|
84
|
+
|
85
|
+
$ta.css({
|
86
|
+
overflow: hidden,
|
87
|
+
overflowY: hidden,
|
88
|
+
wordWrap: 'break-word',
|
89
|
+
resize: resize
|
90
|
+
}).data('autosize', true);
|
91
|
+
|
92
|
+
function initMirror() {
|
93
|
+
mirrored = ta;
|
94
|
+
mirror.className = options.className;
|
95
|
+
|
96
|
+
// mirror is a duplicate textarea located off-screen that
|
97
|
+
// is automatically updated to contain the same text as the
|
98
|
+
// original textarea. mirror always has a height of 0.
|
99
|
+
// This gives a cross-browser supported way getting the actual
|
100
|
+
// height of the text, through the scrollTop property.
|
101
|
+
$.each(copyStyle, function(i, val){
|
102
|
+
mirror.style[val] = $ta.css(val);
|
103
|
+
});
|
104
|
+
}
|
105
|
+
|
106
|
+
// Using mainly bare JS in this function because it is going
|
107
|
+
// to fire very often while typing, and needs to very efficient.
|
108
|
+
function adjust() {
|
109
|
+
var height, overflow, original;
|
110
|
+
|
111
|
+
if (mirrored !== ta) {
|
112
|
+
initMirror();
|
113
|
+
}
|
114
|
+
|
115
|
+
// the active flag keeps IE from tripping all over itself. Otherwise
|
116
|
+
// actions in the adjust function will cause IE to call adjust again.
|
117
|
+
if (!active) {
|
118
|
+
active = true;
|
119
|
+
mirror.value = ta.value + options.append;
|
120
|
+
mirror.style.overflowY = ta.style.overflowY;
|
121
|
+
original = parseInt(ta.style.height,10);
|
122
|
+
|
123
|
+
// Update the width in case the original textarea width has changed
|
124
|
+
// A floor of 0 is needed because IE8 returns a negative value for hidden textareas, raising an error.
|
125
|
+
mirror.style.width = Math.max($ta.width(), 0) + 'px';
|
126
|
+
|
127
|
+
if (supportsScrollHeight) {
|
128
|
+
height = mirror.scrollHeight;
|
129
|
+
} else { // IE6 & IE7
|
130
|
+
mirror.scrollTop = 0;
|
131
|
+
mirror.scrollTop = 9e4;
|
132
|
+
height = mirror.scrollTop;
|
133
|
+
}
|
134
|
+
|
135
|
+
var maxHeight = parseInt($ta.css('maxHeight'), 10);
|
136
|
+
// Opera returns '-1px' when max-height is set to 'none'.
|
137
|
+
maxHeight = maxHeight && maxHeight > 0 ? maxHeight : 9e4;
|
138
|
+
if (height > maxHeight) {
|
139
|
+
height = maxHeight;
|
140
|
+
overflow = 'scroll';
|
141
|
+
} else if (height < minHeight) {
|
142
|
+
height = minHeight;
|
143
|
+
}
|
144
|
+
height += boxOffset;
|
145
|
+
ta.style.overflowY = overflow || hidden;
|
146
|
+
|
147
|
+
if (original !== height) {
|
148
|
+
ta.style.height = height + 'px';
|
149
|
+
if (callback) {
|
150
|
+
options.callback.call(ta,ta);
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
// This small timeout gives IE a chance to draw it's scrollbar
|
155
|
+
// before adjust can be run again (prevents an infinite loop).
|
156
|
+
setTimeout(function () {
|
157
|
+
active = false;
|
158
|
+
}, 1);
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
if (onpropertychange in ta) {
|
163
|
+
if (oninput in ta) {
|
164
|
+
// Detects IE9. IE9 does not fire onpropertychange or oninput for deletions,
|
165
|
+
// so binding to onkeyup to catch most of those occassions. There is no way that I
|
166
|
+
// know of to detect something like 'cut' in IE9.
|
167
|
+
ta[oninput] = ta.onkeyup = adjust;
|
168
|
+
} else {
|
169
|
+
// IE7 / IE8
|
170
|
+
ta[onpropertychange] = adjust;
|
171
|
+
}
|
172
|
+
} else {
|
173
|
+
// Modern Browsers
|
174
|
+
ta[oninput] = adjust;
|
175
|
+
}
|
176
|
+
|
177
|
+
$(window).on('resize', function(){
|
178
|
+
active = false;
|
179
|
+
adjust();
|
180
|
+
});
|
181
|
+
|
182
|
+
// Allow for manual triggering if needed.
|
183
|
+
$ta.on('autosize', function(){
|
184
|
+
active = false;
|
185
|
+
adjust();
|
186
|
+
});
|
187
|
+
|
188
|
+
// Call adjust in case the textarea already contains text.
|
189
|
+
adjust();
|
190
|
+
});
|
191
|
+
};
|
192
|
+
}(window.jQuery || window.Zepto));
|