i18n_locale_editor 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Adam Kliment
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,73 @@
1
+ = Rails i18n locale editor
2
+
3
+ Proof of concept for live locale editor fo Ruby on Rails
4
+
5
+
6
+ == Features
7
+ * edit translations in-place in overlay window right on your live site
8
+ * cool firebug-like translation key selector for localized strings
9
+ * ajax lazy updating for better user experience
10
+ * taranslations listing (index)
11
+ * select which locales to show in translations index
12
+ * show only keys with missing translations
13
+ * unobtrusive JS inclusion - if jQuery is not included in project and async autoloads it from CDN as noConflict(), then loads itself
14
+ * tested in Firefox and Chromium
15
+ * tested on rails 2.3.8, 2.3.2 with bundler see: http://gembundler.com/rails23.html
16
+ * not designed as production CMS, it's for developers
17
+
18
+ == Installation
19
+
20
+ add to your Gemfile:
21
+
22
+ <tt>gem "i18n_locale_editor"</tt>
23
+
24
+ run:
25
+
26
+ <tt>bundle install</tt>
27
+
28
+ add to your layout's <HEAD> section:
29
+
30
+ <tt>=include_i18n_editor</tt>
31
+
32
+ start server with environment variable:
33
+
34
+ <tt>I18N_EDITOR=true script/server</tt>
35
+
36
+
37
+ == TODO
38
+
39
+ * tests
40
+ * code review and documentation
41
+ * rails 3 support
42
+ * test it on real-life translator's workflow to save clicks and improve efficiency (maybe add 'commit and next' to editor)
43
+ * auto copy js asset and include by gem version in init.rb
44
+ * when open in editor is requested, add locale from key to be shown on index if it is not set in session yet
45
+ * when open in editor is requeseed and "show only missing" is enabled, check if the requested key is in the result set, if not cancel the fileter and retry
46
+ * it will copy javascript assets to RAILS_ROOT/public/javascripts/ on every init, find some better way
47
+ * raise if not vaild html accepted as transalation
48
+ * show key name as title in modal window
49
+ * move all javascript functions to i18n namespace to prevent colision
50
+ * make hash_tools as inherited class ExtendedHash from Hash (eradicate monkey patching)
51
+ * extract translation controller name, model name, routing nammespace to some config and make it dynamic
52
+ * ESC to close modal window
53
+ * possibility to load another translations in modal
54
+ * toggle wym editor in modal (cool)
55
+ * change localization key from id to rel in span I18n monkey patch
56
+ * escape path for eval in hash_tools.rb (!!!)
57
+ * translation workflow design
58
+ * translation key is held in span id="en.translation.key". check if the 'id' attribute can contain dots
59
+ * title, buttons, select options are broken - filter them out
60
+ * create tools for refactoring - especially path rename
61
+ * paginantion on index
62
+ * list missing translations
63
+ * api for external access
64
+ * scopes support for api (how)
65
+ * export all locales to csv (it's done, but not included in this gem)
66
+ * import all locales from csv (it's done, but not included in this gem)
67
+ * key hitcounter
68
+ * test in Opera, IE and other browsers...
69
+ * review these issues and move them to the github's task tracker :)
70
+
71
+ == Copyright
72
+
73
+ Copyright (c) 2011 Adam Kliment, Virtualmater s.r.o. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "i18n_locale_editor"
8
+ gem.summary = %Q{Rails i18n locale editor}
9
+ gem.description = %Q{Rails i18n locale web editor as plugin}
10
+ gem.email = "adam.kliment@virtualmaster.cz"
11
+ gem.homepage = "http://github.com/n1k/i18n_locale_editor"
12
+ gem.authors = ["Adam Kliment"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ gem.add_dependency "ya2yaml", ">=0.26"
15
+ gem.add_dependency "rails", ">= 2.3.0"
16
+ gem.add_dependency "i18n", ">= 0"
17
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
+ end
19
+ Jeweler::GemcutterTasks.new
20
+ rescue LoadError
21
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
22
+ end
23
+
24
+ require 'spec/rake/spectask'
25
+ Spec::Rake::SpecTask.new(:spec) do |spec|
26
+ spec.libs << 'lib' << 'spec'
27
+ spec.spec_files = FileList['spec/**/*_spec.rb']
28
+ end
29
+
30
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
31
+ spec.libs << 'lib' << 'spec'
32
+ spec.pattern = 'spec/**/*_spec.rb'
33
+ spec.rcov = true
34
+ end
35
+
36
+ task :spec => :check_dependencies
37
+
38
+ task :default => :spec
39
+
40
+ require 'rake/rdoctask'
41
+ Rake::RDocTask.new do |rdoc|
42
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
43
+
44
+ rdoc.rdoc_dir = 'rdoc'
45
+ rdoc.title = "i18n_locale_editor #{version}"
46
+ rdoc.rdoc_files.include('README*')
47
+ rdoc.rdoc_files.include('lib/**/*.rb')
48
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,45 @@
1
+ class TranslationsController < ApplicationController
2
+ before_filter :set_language, :except => [:index]
3
+ skip_before_filter :verify_authenticity_token, :only => [:set_value]
4
+ #TODO remove
5
+ skip_before_filter :admin_required, :login_required
6
+ layout 'translations'
7
+
8
+
9
+ def index
10
+ session[:i18n_locale_editor_translations] = [I18n.available_locales.first] if session[:i18n_locale_editor_translations].blank?
11
+ session[:i18n_show_only_incomplete] = false if session[:i18n_show_only_incomplete].blank?
12
+
13
+ @translations = Translation.merge_locales session[:i18n_locale_editor_translations], session[:i18n_show_only_incomplete]
14
+
15
+ respond_to do |format|
16
+ format.html # index.html.erb
17
+ format.xml { render :xml => @translations }
18
+ end
19
+ end
20
+
21
+ def get_value
22
+ value = Translation.all(@language).find_by_path(params[:path])
23
+ render :text => value
24
+ end
25
+
26
+ def set_value
27
+ Translation.update_by_path(@language, params[:path], params[:value])
28
+ I18n.reload!
29
+ render :text => "OK"
30
+ end
31
+
32
+ def set_translations
33
+ session[:i18n_locale_editor_translations] = params[:translations]
34
+ session[:i18n_show_only_incomplete] = true if params[:show_only_incomplete] == "true"
35
+ session[:i18n_show_only_incomplete] = false if params[:show_only_incomplete] == "false"
36
+ redirect_to :action => 'index'
37
+ end
38
+
39
+ private
40
+
41
+ def set_language
42
+ @language = params[:path].split(".").first if not params[:path].nil?
43
+ # @language = I18n.locale if not I18n.available_locales.include? @language
44
+ end
45
+ end
@@ -0,0 +1,53 @@
1
+ # Methods added to this helper will be available to all templates in the application.
2
+ module TranslationsHelper
3
+ def include_i18n_editor
4
+ return nil if not ENV['I18N_EDITOR'].to_s == "true"
5
+ javascript = "
6
+ // i18n locale editor
7
+ // =================
8
+ // - lazy loads jQuery from jQuery's CDN if it is not defined and declares it as $j if $js is undefined
9
+ // - waits for jQuery initialization and then lazy-loads itself
10
+ // - it expect i18n-locale-editor.js in /public/javascripts
11
+
12
+ i18n_locale = '#{I18n.locale.to_s}';
13
+
14
+ if(typeof($j) === 'undefined'){
15
+ var $j;
16
+ }
17
+
18
+ function i18n_lazy_load_js(url){
19
+ var s = document.createElement('script');
20
+ s.type = 'text/javascript';
21
+ s.async = true;
22
+ s.src = url;
23
+ var x = document.getElementsByTagName('script')[0];
24
+ x.parentNode.insertBefore(s, x);
25
+ }
26
+
27
+ function i18n_lazy_loader(){
28
+ if(! (typeof(jQuery) === 'function')){
29
+ i18n_lazy_load_js('http://code.jquery.com/jquery-1.5.1.min.js')
30
+ }
31
+ interval = setInterval(function(){
32
+ if(typeof(jQuery) === 'function'){
33
+ if(typeof($j) === 'undefined'){
34
+ $j = jQuery.noConflict();
35
+ }
36
+ i18n_lazy_load_js('/javascripts/i18n-locale-editor.js')
37
+ clearInterval(interval);
38
+ }
39
+ },100)
40
+
41
+ }
42
+
43
+ if(window.attachEvent) {
44
+ window.attachEvent('onload', i18n_lazy_loader);
45
+ } else {
46
+ window.addEventListener('load', i18n_lazy_loader, false);
47
+ }
48
+
49
+ "
50
+ javascript_tag javascript
51
+ end
52
+ end
53
+ ActionView::Base.send :include, TranslationsHelper
@@ -0,0 +1,59 @@
1
+ #LOCED
2
+ require 'ya2yaml'
3
+ require 'fileutils'
4
+
5
+ class Translation
6
+
7
+ def self.all(locale)
8
+ locale = locale.to_s
9
+ file = File.join(RAILS_ROOT, "config/locales","#{locale}.yml")
10
+ hash = YAML::load_file file
11
+ end
12
+
13
+ def self.update_by_path(locale,path,value)
14
+ locale = locale.to_s
15
+
16
+ file = File.join(RAILS_ROOT, "config/locales","#{locale}.yml")
17
+ hash = YAML::load_file file
18
+ hash.update_by_path(path,value)
19
+ File.open(file + ".new",'w') do |f|
20
+ f.puts hash.ya2yaml
21
+ end
22
+ FileUtils.mv(file + ".new", file)
23
+ end
24
+
25
+ def self.merge_locales locales_array, only_missing = false
26
+ #available_locales = I18n.available_locales.map{|loc| loc.to_s}
27
+ #locales_array.each{|loc| raise "#{loc} locale not found" if not available_locales.include? loc.to_s }
28
+
29
+ paths = []
30
+ translations = {}
31
+ locales_array.each do |locale|
32
+ translations[locale] = translation = Translation.all(locale)
33
+ paths = paths + translation.paths.map{|path| path.gsub(/^#{locale.to_s}\./,"")}
34
+ end
35
+ paths = paths.uniq
36
+
37
+ aggreg = {}
38
+ paths.each do |path|
39
+ aggreg[path] = {}
40
+ locales_array.each do |locale|
41
+ aggreg[path][locale] = translations[locale].find_by_path(locale.to_s + '.' + path)
42
+ end
43
+ end
44
+ if only_missing
45
+ return aggreg.delete_if do |path,translation|
46
+ ret = true
47
+ translation.each do |language, value|
48
+ ret = false if value.blank?
49
+ end
50
+
51
+ ret
52
+ end
53
+ else
54
+ return aggreg
55
+ end
56
+
57
+ end
58
+ end
59
+
@@ -0,0 +1,27 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html lang="en-US" xml:lang="en-US" xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>Translation editor</title>
5
+ <meta content="text/html;charset=UTF-8" http-equiv="content-type"/>
6
+ <style media='all' type='text/css'>
7
+ body{ font-family: Verdana,Arial,sans-serif; border: 0; margin: 0; padding: 0;}
8
+ .i18n-translation-editor{padding: 10px}
9
+ .i18n-translation-key{font-size: 20px; padding-top: 10px}
10
+ .i18n-locale-label{:font-weight: bold; font-size: 30px; background-color: #111; color: #fff}
11
+ .i18n-translation-missing{}
12
+ .i18n-translation-cell{cursor: default}
13
+ .i18n-highlighted-cell{background-color: #eee;color:#000}
14
+ .i18n-highlighted-exclamation{font-size: 40px; color: #111; font-weight: bold; padding-left: 10px}
15
+ </style>
16
+
17
+ <%=include_i18n_editor%>
18
+
19
+ </head>
20
+
21
+ <body>
22
+ <div class="i18n-translation-editor">
23
+ <%= yield%>
24
+ </div>
25
+ </body>
26
+ </html>
27
+
@@ -0,0 +1,42 @@
1
+ <h1>Listing translations</h1>
2
+
3
+ <%form_tag url_for({:action => 'set_translations'}), :method => :post do |form|%>
4
+ <%I18n.available_locales.each do |locale|%>
5
+ <%= locale%>
6
+ <%= check_box_tag "translations[]", locale, session[:i18n_locale_editor_translations].include?(locale.to_s) ? true : false %>
7
+ <%end%>
8
+ only incomplete rows
9
+ <%= hidden_field_tag "show_only_incomplete", "false"%>
10
+ <%= check_box_tag "show_only_incomplete","true", session[:i18n_show_only_incomplete]%>
11
+ <%= submit_tag :show%>
12
+ <%end%>
13
+ <table>
14
+ <tr>
15
+ <%session[:i18n_locale_editor_translations].each do |locale|%>
16
+ <th class="i18n-locale-label"><%= locale%></th>
17
+ <%end%>
18
+ </tr>
19
+ <% @translations.each do |path, translations| %>
20
+ <tr>
21
+ <td colspan="<%=translations.length%>">
22
+ <div class="i18n-translation-key">
23
+ <%= path%>
24
+ </div>
25
+ </td>
26
+ </tr>
27
+ <tr>
28
+ <%translations.each do |language, translation|%>
29
+ <td class="i18n-translation-cell <%='i18n-translation-missing' if translation.blank?%>">
30
+ <div>
31
+ <span class="i18n-translation" id="<%=language.to_s + '.' + path%>"><%=translation%></span>
32
+ <%if translation.blank?%>
33
+ <span class="i18n-highlighted-exclamation">!</span>
34
+ <%end%>
35
+ </div>
36
+ </td>
37
+ <%end%>
38
+ </tr>
39
+ <% end %>
40
+ </table>
41
+ <br />
42
+
data/config/routes.rb ADDED
@@ -0,0 +1,5 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.translations 'translations/get_value/:path', :controller => 'translations', :action => 'get_value', :method => :get, :requirements => { :path => /.+/ }
3
+ map.translations 'translations/set_value/:path', :controller => 'translations', :action => 'set_value', :method => :get, :requirements => { :path => /.+/ }
4
+ map.translations 'translations/index', :controller => 'translations', :action => 'index'
5
+ end
data/lib/hash_tools.rb ADDED
@@ -0,0 +1,105 @@
1
+ class Hash
2
+ #Finds value in hash by path
3
+ #example:
4
+ #hash = {
5
+ # a => 'foo',
6
+ # b => {
7
+ # ba => {
8
+ # bac => 'bar'
9
+ # }
10
+ # }
11
+ #}
12
+ #>>>
13
+
14
+ def find_by_path(path)
15
+ e ="self"
16
+ path_to_a(path).each{|i| e = e+"['#{i}']" }
17
+ begin
18
+ eval(e)
19
+ rescue NoMethodError
20
+ return nil
21
+ end
22
+ end
23
+
24
+
25
+ def update_by_path(path, string)
26
+ return false if find_by_path(path).class == "Hash"
27
+ create_path_if_not_exist(path)
28
+ e ="self"
29
+ path_a = path_to_a(path)
30
+ path_a.each{|i| e = e+"['#{i}']" }
31
+ string = string.gsub(/^[ \t]+/,"")
32
+ e = e+"='#{string}'"
33
+ begin
34
+ eval(e)
35
+ return true
36
+ rescue => e
37
+ return [false, e]
38
+ end
39
+ end
40
+
41
+
42
+ def update_by_path_wo_parents(path, string)
43
+ return false if find_by_path(path).class.to_s == "Hash" && find_by_path(path).length > 0
44
+ e ="self"
45
+ path_a = path_to_a(path)
46
+ path_a.each{|i| e = e+"['#{i}']" }
47
+ #string = string.gsub(/^[ \t]+/,"")
48
+ e = e+"=string"
49
+ begin
50
+ eval(e)
51
+ return true
52
+ rescue
53
+ return false
54
+ end
55
+ end
56
+
57
+ def create_empty_node(path)
58
+ return false if not find_by_path(path).nil?
59
+ update_by_path_wo_parents(path, {})
60
+ end
61
+
62
+ def create_path_if_not_exist(path)
63
+ aa = path_to_a(path)
64
+ new_aa = []
65
+ aa.each do |a|
66
+ new_aa << a
67
+ create_empty_node(a_to_path(new_aa))
68
+ end
69
+ end
70
+
71
+ def nodes_array(path = [])
72
+ ret = []
73
+ self.each do |key,value|
74
+ if value.class == Hash
75
+ ret = ret + value.nodes_array(path + [key])
76
+ else
77
+ ret << path + [key]
78
+ end
79
+ end
80
+ ret
81
+ end
82
+
83
+ def paths
84
+ self.nodes_array.map{|i| i.join(".")}
85
+ end
86
+
87
+ def next_path(path)
88
+ paths = self.paths
89
+ paths[paths.index(path) + 1]
90
+ end
91
+
92
+ def previous_path(path)
93
+ paths = self.paths
94
+ paths[paths.index(path) - 1]
95
+ end
96
+
97
+ private
98
+ def path_to_a(string)
99
+ string.split(".")
100
+ end
101
+
102
+ def a_to_path(array)
103
+ array.join(".")
104
+ end
105
+ end
@@ -0,0 +1,20 @@
1
+ require 'hash_tools'
2
+ require 'translate_monkey_patch'
3
+ require 'ftools'
4
+
5
+ if ENV['I18N_EDITOR']== "true"
6
+ src = File.join(directory,'public',"i18n-locale-editor.js")
7
+ dst = File.join(RAILS_ROOT,'public', 'javascripts')
8
+ File.copy(src,dst)
9
+ end
10
+
11
+ %w{ models controllers helpers }.each do |dir|
12
+ path = File.join(directory, 'app',dir)
13
+ $LOAD_PATH << path
14
+ ActiveSupport::Dependencies.load_paths << path
15
+ ActiveSupport::Dependencies.load_once_paths.delete(path)
16
+ end
17
+
18
+ require File.join(directory, "app", "helpers", "translations_helper.rb")
19
+ ActionView::Base.send :include, TranslationsHelper;
20
+
@@ -0,0 +1,37 @@
1
+ # Don't run in production, it may cause death or serios injury to your layout. :-P
2
+ #TODO
3
+ #wrap tag helpers
4
+ # def text_field
5
+ # /usr/lib/ruby/gems/1.8/gems/actionpack-2.3.8/lib/action_view/helpers/form_helper.rb
6
+ # def text_field_tag
7
+ # /usr/lib/ruby/gems/1.8/gems/actionpack-2.3.8/lib/action_view/helpers/form_tag_helper.rb:
8
+
9
+ if ENV['I18N_EDITOR'].to_s == "true"
10
+ module ActionView
11
+ module Helpers
12
+ module TranslationHelper
13
+ alias :old_translate :translate
14
+
15
+ def translate(*args)
16
+ key = [*args].first
17
+ key = I18n.locale.to_s + '.' + key.to_s
18
+ translation = old_translate(*args)
19
+ translation = "&nbsp;" if translation.blank?
20
+ '<span id="'+key+'" class="i18n-translation">' + translation + '</span>'
21
+ end
22
+ alias :t :translate
23
+ end
24
+ end
25
+ end
26
+ else
27
+ module ActionView
28
+ module Helpers
29
+ module TranslationHelper
30
+ def include_i18n_tools
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+
@@ -0,0 +1,167 @@
1
+ if(typeof $j === 'undefined'){
2
+ $j = jQuery.noConflict();
3
+ }
4
+
5
+ function attach_callbacks() {
6
+ $j('.i18n-translation').each(function(){
7
+ $j(this).bind('click.i18n_selector',function(event){
8
+ event.preventDefault();
9
+ fill_modal($j(this));
10
+ show_modal();
11
+ return false;
12
+ }).bind('mouseover.i18n_selector',
13
+ function(){
14
+ $j(this).addClass('i18n-highlighted')
15
+ $j('#i18n-text').html($j(this).attr('id'))
16
+ }).bind('mouseout.i18n_selector', function(){
17
+ $j(this).removeClass('i18n-highlighted')
18
+ $j('#i18n-text').html('none')
19
+ })
20
+ red_button();
21
+ })
22
+
23
+ }
24
+
25
+ function detach_callbacks() {
26
+ $j('.i18n-translation').unbind('click.i18n_selector').unbind('mouseover.i18n_selector').unbind('mouseout.i18n_selector');
27
+ $j('#i18n-button').css('background-color', 'blue');
28
+ blue_button();
29
+ }
30
+
31
+ function blue_button() {
32
+ $j('#i18n-button').bind('click.i18n_attach_callbacks',function(){
33
+ attach_callbacks();
34
+ return false;
35
+ }).html('SELECT')
36
+ }
37
+
38
+ function red_button(){
39
+ $j('#i18n-button').css('background-color', 'red').html('STOP')
40
+ $j('#i18n-button').unbind('click.i18n_attach_callbacks')
41
+ $j('#i18n-button').bind('click.i18n_detach_callbacks',function(){
42
+ detach_callbacks();
43
+ return false;
44
+ })
45
+
46
+ }
47
+
48
+ function hide_modal(){
49
+ $j('#i18n-mask, .i18n-window').hide();
50
+ }
51
+
52
+ function fill_modal(element){
53
+ url = "/translations/index"
54
+ content = '\
55
+ <input type="hidden" name="i18n-form-key" value="'+element.attr('id')+'" id="i18n-form-key">\
56
+ <form onsubmit="i18n_send_form();return false;">\
57
+ <textarea name="i8n-form-value" id="i18n-form-value">'+element.html()+'</textarea>\
58
+ </form>\
59
+ <div><a href="#" onclick="i18n_send_form();return false;">commit change</a></div\
60
+ <div><a href="'+url+'#'+ element.attr('id') +'" target="_blank">open in editor</a></div>\
61
+ '
62
+ $j('#18n-modal-content').html(content)
63
+ }
64
+
65
+ function i18n_send_form(){
66
+ key = $j('#i18n-form-key').val();
67
+ value = $j('#i18n-form-value').val();
68
+ selector = 'span[id="'+key+'"]';
69
+ $j(selector).html(value);
70
+ update_value(key, value)
71
+ hide_modal();
72
+ }
73
+
74
+ function update_value(key,value) {
75
+ $j('span[id="'+key+'"]').siblings(".i18n-highlighted-exclamation").hide()
76
+ $j('#i18n-working').show()
77
+ url= '/translations/set_value/'+key
78
+ request = $j.ajax({
79
+ // GET method to be able pass redirects
80
+ type: 'GET',
81
+ url: url,
82
+ data: {value: value},
83
+ async: true,
84
+ success: function(){
85
+ $j('#i18n-working').hide();
86
+
87
+ },
88
+ error: function(){
89
+ alert('error posting data to url:'+url);
90
+ $j('#i18n-working').hide()
91
+ }
92
+ })
93
+ }
94
+ function show_modal(){
95
+ element = $j("#i18n-dialog");
96
+ var maskHeight = $j(document).height();
97
+ var maskWidth = $j(window).width();
98
+ $j('#i18n-mask').css({'width':maskWidth,'height':maskHeight});
99
+ $j('#i18n-mask').show();
100
+ var winH = $j(window).height();
101
+ var winW = $j(window).width();
102
+ element.css('top', winH/2-element.height()/2);
103
+ element.css('left', winW/2-element.width()/2);
104
+ element.show()
105
+ }
106
+
107
+ function i18n_initialize_selectors() {
108
+ add_assets();
109
+ $j("#i18n-button").show()
110
+ blue_button();
111
+ $j('#i18n-modal-close').click(function(){
112
+ hide_modal();
113
+ })
114
+ }
115
+
116
+ function i18n_initialize_editor(){
117
+ add_assets();
118
+ $j('.i18n-translation-cell').click(function(){
119
+ fill_modal($j('.i18n-translation', this));
120
+ show_modal();
121
+ }).hover(function(){
122
+ $j(this).addClass('i18n-highlighted-cell');
123
+ }, function(){
124
+ $j(this).removeClass('i18n-highlighted-cell');
125
+ }
126
+ )
127
+ }
128
+
129
+ function add_assets() {
130
+ $j('\
131
+ <div id= "i18n-placeholder" >\
132
+ <div id="i18n-text">&nbsp;</div>\
133
+ <a id="i18n-button" href="#" style="display: none">&nbsp;</a>\
134
+ <div id="i18n-working" style="display: none">Zzz...</div>\
135
+ </div>\
136
+ \
137
+ <div id="i18n-boxes">\
138
+ <div id="i18n-dialog" class="i18n-window">\
139
+ <div id="18n-modal-content"></div>\
140
+ <a href="#" id="i18n-modal-close" onclick="hide_modal();return false;">close window</a>\
141
+ </div>\
142
+ <div id="i18n-mask"></div>\
143
+ </div>\
144
+ ').appendTo("body")
145
+ $j('\
146
+ <style type="text/css"> \
147
+ .i18n-highlighted{ cursor: default; outline: 3px solid yellow} \
148
+ #i18n-text{ text-align: right; }\
149
+ #i18n-working{ text-align: center;line-height: 30px;width: 50px; height: 30px; float: right; background-color: #444; color: white;}\
150
+ #i18n-button{ text-align: center;line-height: 30px;width: 50px; height: 30px; float: right; background-color: #111; color: white;}\
151
+ #i18n-button a{ color: white;}\
152
+ #i18n-button a:hover{ color: white;}\
153
+ #i18n-placeholder{font-size: 12px;font-family: Verdana, Arial, sans-serif; margin:auto; position: fixed; z-index: 10000; right: 0px; bottom: 0px;}\
154
+ #i18n-mask { position:absolute; top: 0; z-index:9998; background-color:#000; display:none;opacity:0.4;filter:alpha(opacity=40) } \
155
+ #i18n-boxes .i18n-window { font-size: 12px;font-family: Verdana, Arial, sans-serif;position:fixed;width:600px;height:400px;display:none;z-index:9999;padding:20px;} \
156
+ #i18n-boxes #i18n-dialog { width:580px; height:400px; background-color: white; }\
157
+ #i18n-boxes #i18n-dialog textarea { width:560px; height:300px; background-color: white; }\
158
+ </style> \
159
+ ').appendTo("head");
160
+ }
161
+
162
+ if(! ($j('.i18n-translation-editor').length == 0)){
163
+ i18n_initialize_editor();
164
+ } else{
165
+ i18n_initialize_selectors();
166
+ }
167
+
data/rails/init.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'hash_tools'
2
+ require 'translate_monkey_patch'
3
+ require 'ftools'
4
+
5
+ if ENV['I18N_EDITOR']== "true"
6
+ src = File.join(directory,'public',"i18n-locale-editor.js")
7
+ dst = File.join(RAILS_ROOT,'public', 'javascripts')
8
+ File.copy(src,dst)
9
+ end
10
+
11
+ %w{ models controllers helpers }.each do |dir|
12
+ path = File.join(directory, 'app',dir)
13
+ $LOAD_PATH << path
14
+ ActiveSupport::Dependencies.load_paths << path
15
+ ActiveSupport::Dependencies.load_once_paths.delete(path)
16
+ end
17
+
18
+ require File.join(directory, "app", "helpers", "translations_helper.rb")
19
+ ActionView::Base.send :include, TranslationsHelper;
20
+
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "I18nLocaleEditor" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'i18n_locale_editor'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: i18n_locale_editor
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Adam Kliment
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-07 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 1
32
+ - 2
33
+ - 9
34
+ version: 1.2.9
35
+ type: :development
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: ya2yaml
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 63
46
+ segments:
47
+ - 0
48
+ - 26
49
+ version: "0.26"
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: rails
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 2
63
+ - 3
64
+ - 0
65
+ version: 2.3.0
66
+ type: :runtime
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ name: i18n
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ type: :runtime
81
+ version_requirements: *id004
82
+ description: Rails i18n locale web editor as plugin
83
+ email: adam.kliment@virtualmaster.cz
84
+ executables: []
85
+
86
+ extensions: []
87
+
88
+ extra_rdoc_files:
89
+ - LICENSE
90
+ - README.rdoc
91
+ files:
92
+ - .document
93
+ - .gitignore
94
+ - LICENSE
95
+ - README.rdoc
96
+ - Rakefile
97
+ - VERSION
98
+ - app/controllers/translations_controller.rb
99
+ - app/helpers/translations_helper.rb
100
+ - app/models/translation.rb
101
+ - app/views/layouts/translations.erb
102
+ - app/views/translations/index.html.erb
103
+ - config/routes.rb
104
+ - lib/hash_tools.rb
105
+ - lib/i18n_locale_editor.rb
106
+ - lib/translate_monkey_patch.rb
107
+ - public/i18n-locale-editor.js
108
+ - rails/init.rb
109
+ - spec/i18n_locale_editor_spec.rb
110
+ - spec/spec.opts
111
+ - spec/spec_helper.rb
112
+ has_rdoc: true
113
+ homepage: http://github.com/n1k/i18n_locale_editor
114
+ licenses: []
115
+
116
+ post_install_message:
117
+ rdoc_options:
118
+ - --charset=UTF-8
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ hash: 3
127
+ segments:
128
+ - 0
129
+ version: "0"
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ hash: 3
136
+ segments:
137
+ - 0
138
+ version: "0"
139
+ requirements: []
140
+
141
+ rubyforge_project:
142
+ rubygems_version: 1.3.7
143
+ signing_key:
144
+ specification_version: 3
145
+ summary: Rails i18n locale editor
146
+ test_files:
147
+ - spec/i18n_locale_editor_spec.rb
148
+ - spec/spec_helper.rb