i18n_locale_editor 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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